diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c280eeecc04..abf14543477 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,12 @@ name: CI on: + push: + branches: + - master pull_request: branches: - master + jobs: run_linters: name: Run Linters @@ -47,6 +51,7 @@ jobs: source $HOME/BYOND/byond/bin/byondsetup tools/ci/generate_maplist.sh tools/ci/dm.sh -Mci_map_testing paradise.dme + unit_tests_and_sql: name: Unit Tests + SQL Validation runs-on: ubuntu-18.04 @@ -79,7 +84,6 @@ jobs: - name: Compile & Run Unit Tests run: | tools/ci/install_byond.sh - tools/ci/install_rustg.sh source $HOME/BYOND/byond/bin/byondsetup tools/ci/dm.sh -DCIBUILDING paradise.dme tools/ci/run_server.sh diff --git a/_maps/map_files/cyberiad/z2.dmm b/_maps/map_files/cyberiad/z2.dmm index 06c24901cd9..d69f3f5a286 100644 --- a/_maps/map_files/cyberiad/z2.dmm +++ b/_maps/map_files/cyberiad/z2.dmm @@ -12341,7 +12341,7 @@ /area/holodeck/source_picnicarea) "QV" = ( /obj/item/clothing/under/rainbow, -/obj/item/clothing/glasses/sunglasses, +/obj/item/clothing/glasses/sunglasses_fake, /turf/simulated/floor/beach/sand, /area/holodeck/source_beach) "Rf" = ( diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index ee16b4d59f4..696d6fdf105 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -49,10 +49,11 @@ #define PREFTOGGLE_2_ANONDCHAT 16 #define PREFTOGGLE_2_AFKWATCH 32 #define PREFTOGGLE_2_RUNECHAT 64 +#define PREFTOGGLE_2_DEATHMESSAGE 128 -#define TOGGLES_2_TOTAL 127 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined. +#define TOGGLES_2_TOTAL 255 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined. -#define TOGGLES_2_DEFAULT (PREFTOGGLE_2_FANCYUI|PREFTOGGLE_2_ITEMATTACK|PREFTOGGLE_2_WINDOWFLASHING|PREFTOGGLE_2_RUNECHAT) +#define TOGGLES_2_DEFAULT (PREFTOGGLE_2_FANCYUI|PREFTOGGLE_2_ITEMATTACK|PREFTOGGLE_2_WINDOWFLASHING|PREFTOGGLE_2_RUNECHAT|PREFTOGGLE_2_DEATHMESSAGE) // Sanity checks #if TOGGLES_TOTAL > 16777215 diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm index ad6f95a8654..7fdd53dde30 100644 --- a/code/__DEFINES/rust_g.dm +++ b/code/__DEFINES/rust_g.dm @@ -12,7 +12,7 @@ #define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y) -// Git related operations // - AA TODO: Add in in-code revision parsing +// Git related operations // #define rustg_git_revparse(rev) call(RUST_G, "rg_git_revparse")(rev) #define rustg_git_commit_date(rev) call(RUST_G, "rg_git_commit_date")(rev) diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 07c5cefbdb3..4652ee2f20b 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -52,7 +52,7 @@ add_attack_logs(src, src, "[key_name_admin(src)] might be running a modified client! (failed can_see on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))", ATKLOG_ALL) var/message = "[key_name(src)] might be running a modified client! (failed can_see on AI click of [A]([COORD(pixel_turf)]))" log_admin(message) - SSdiscord.send2discord_simple_noadmins("[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") + SSdiscord.send2discord_simple_noadmins("**\[Warning]** [key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") var/turf_visible @@ -65,7 +65,7 @@ if(pixel_turf.obscured) log_admin("[key_name_admin(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)])") add_attack_logs(src, src, "[key_name_admin(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))", ATKLOG_ALL) - SSdiscord.send2discord_simple_noadmins("[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") + SSdiscord.send2discord_simple_noadmins("**\[Warning]** [key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") return var/list/modifiers = params2list(params) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 7c54ef79a06..1a89a4b7fd5 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -252,19 +252,22 @@ /// Role ID to be pinged for administrative events var/discord_admin_role_id = null // Intentional null usage - /// Webhook URL for the main public webhook - var/discord_main_webhook_url + /// Webhook URLs for the main public webhook + var/list/discord_main_webhook_urls = list() - /// Webhook URL for the admin webhook - var/discord_admin_webhook_url + /// Webhook URLs for the admin webhook + var/list/discord_admin_webhook_urls = list() - /// Webhook URL for the mentor webhook - var/discord_mentor_webhook_url + /// Webhook URLs for the mentor webhook + var/list/discord_mentor_webhook_urls = list() /// Do we want to forward all adminhelps to the discord or just ahelps when admins are offline. /// (This does not mean all ahelps are pinged, only ahelps sent when staff are offline get the ping, regardless of this setting) var/discord_forward_all_ahelps = FALSE + /// URL for the CentCom Ban DB API + var/centcom_ban_db_url = null + /datum/configuration/New() for(var/T in subtypesof(/datum/game_mode)) var/datum/game_mode/M = T @@ -746,14 +749,16 @@ if("discord_webhooks_admin_role_id") discord_admin_role_id = "[value]" // This MUST be a string because BYOND doesnt like massive integers if("discord_webhooks_main_url") - discord_main_webhook_url = value + discord_main_webhook_urls = splittext(value, "|") if("discord_webhooks_admin_url") - discord_admin_webhook_url = value + discord_admin_webhook_urls = splittext(value, "|") if("discord_webhooks_mentor_url") - discord_mentor_webhook_url = value + discord_mentor_webhook_urls = splittext(value, "|") if("discord_forward_all_ahelps") discord_forward_all_ahelps = TRUE // End discord stuff + if("centcom_ban_db_url") + centcom_ban_db_url = value else log_config("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/subsystem/discord.dm b/code/controllers/subsystem/discord.dm index 1360b7f9bfb..a0f8c1a68fe 100644 --- a/code/controllers/subsystem/discord.dm +++ b/code/controllers/subsystem/discord.dm @@ -15,30 +15,32 @@ SUBSYSTEM_DEF(discord) /datum/controller/subsystem/discord/proc/send2discord_simple(destination, content) if(!enabled) return - var/webhook_url + var/list/webhook_urls switch(destination) if(DISCORD_WEBHOOK_ADMIN) - webhook_url = config.discord_admin_webhook_url + webhook_urls = config.discord_admin_webhook_urls if(DISCORD_WEBHOOK_PRIMARY) - webhook_url = config.discord_main_webhook_url + webhook_urls = config.discord_main_webhook_urls if(DISCORD_WEBHOOK_MENTOR) - webhook_url = config.discord_mentor_webhook_url + webhook_urls = config.discord_mentor_webhook_urls var/datum/discord_webhook_payload/dwp = new() dwp.webhook_content = content - SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, webhook_url, dwp.serialize2json(), list("content-type" = "application/json")) + for(var/url in webhook_urls) + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json")) // This one is designed to take in a [/datum/discord_webhook_payload] which was prepared beforehand /datum/controller/subsystem/discord/proc/send2discord_complex(destination, datum/discord_webhook_payload/dwp) if(!enabled) return - var/webhook_url + var/list/webhook_urls switch(destination) if(DISCORD_WEBHOOK_ADMIN) - webhook_url = config.discord_admin_webhook_url + webhook_urls = config.discord_admin_webhook_urls if(DISCORD_WEBHOOK_PRIMARY) - webhook_url = config.discord_main_webhook_url - SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, webhook_url, dwp.serialize2json(), list("content-type" = "application/json")) + webhook_urls = config.discord_main_webhook_urls + for(var/url in webhook_urls) + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json")) // This one is for sending messages to the admin channel if no admins are active, complete with a ping to the game admins role /datum/controller/subsystem/discord/proc/send2discord_simple_noadmins(content, check_send_always = FALSE) @@ -66,8 +68,8 @@ SUBSYSTEM_DEF(discord) var/datum/discord_webhook_payload/dwp = new() dwp.webhook_content = message - - SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, config.discord_admin_webhook_url, dwp.serialize2json(), list("content-type" = "application/json")) + for(var/url in config.discord_admin_webhook_urls) + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json")) // Helper to make administrator ping easier /datum/controller/subsystem/discord/proc/handle_administrator_ping() @@ -79,4 +81,4 @@ SUBSYSTEM_DEF(discord) last_administration_ping = world.time + 60 SECONDS return "<@&[config.discord_admin_role_id]>" - return "*(Role not configured)*" + return "" diff --git a/code/controllers/subsystem/http.dm b/code/controllers/subsystem/http.dm index 3a857f76137..ad5407bfdca 100644 --- a/code/controllers/subsystem/http.dm +++ b/code/controllers/subsystem/http.dm @@ -9,12 +9,17 @@ SUBSYSTEM_DEF(http) var/list/datum/http_request/active_async_requests /// Variable to define if logging is enabled or not. Disabled by default since we know the requests the server is making. Enable with VV if you need to debug requests var/logging_enabled = FALSE + /// Total requests the SS has processed in a round + var/total_requests /datum/controller/subsystem/http/Initialize(start_timeofday) rustg_create_async_http_client() // Open the door active_async_requests = list() return ..() +/datum/controller/subsystem/http/stat_entry() + ..("P: [length(active_async_requests)] | T: [total_requests]") + /datum/controller/subsystem/http/fire(resumed) for(var/r in active_async_requests) var/datum/http_request/req = r @@ -57,6 +62,7 @@ SUBSYSTEM_DEF(http) // Begin it and add it to the SS active list req.begin_async() active_async_requests += req + total_requests++ if(logging_enabled) // Create a log holder diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index d4bbf0fd9e3..2df69094701 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -127,9 +127,10 @@ SUBSYSTEM_DEF(ticker) if((GLOB.master_mode=="random") || (GLOB.master_mode=="secret")) runnable_modes = config.get_runnable_modes() if(runnable_modes.len==0) + to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") + force_start = FALSE current_state = GAME_STATE_PREGAME Master.SetRunLevel(RUNLEVEL_LOBBY) - to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") return 0 if(GLOB.secret_force_mode != "secret") var/datum/game_mode/M = config.pick_mode(GLOB.secret_force_mode) @@ -147,6 +148,7 @@ SUBSYSTEM_DEF(ticker) to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") mode = null current_state = GAME_STATE_PREGAME + force_start = FALSE SSjobs.ResetOccupations() Master.SetRunLevel(RUNLEVEL_LOBBY) return 0 @@ -158,8 +160,9 @@ SUBSYSTEM_DEF(ticker) SSjobs.DivideOccupations() //Distribute jobs if(!can_continue) qdel(mode) - current_state = GAME_STATE_PREGAME to_chat(world, "Error setting up [GLOB.master_mode]. Reverting to pre-game lobby.") + current_state = GAME_STATE_PREGAME + force_start = FALSE SSjobs.ResetOccupations() Master.SetRunLevel(RUNLEVEL_LOBBY) return 0 @@ -276,7 +279,7 @@ SUBSYSTEM_DEF(ticker) //start_events() //handles random events and space dust. //new random event system is handled from the MC. - SSdiscord.send2discord_simple_noadmins("Round has started") + SSdiscord.send2discord_simple_noadmins("**\[Info]** Round has started") auto_toggle_ooc(0) // Turn it off round_start_time = world.time diff --git a/code/datums/discord.dm b/code/datums/discord.dm index 48550b6a7b9..5636d752e5b 100644 --- a/code/datums/discord.dm +++ b/code/datums/discord.dm @@ -1,7 +1,7 @@ -/******************************************************************************************************** -* DO NOT EDIT ANYTHING IN HERE WITHOUT CHECKING THE DISCORD API SPEC AND TESTING THE JSON POST FORMAT * -* OTHERWISE, YOU **WILL** BREAK STUFF -aa ************************************************************* -********************************************/ +/* + DO NOT EDIT ANYTHING IN HERE WITHOUT CHECKING THE DISCORD API SPEC AND TESTING THE JSON POST FORMAT + OTHERWISE, YOU **WILL** BREAK STUFF -aa +*/ /** * # Discord Webhook Payload @@ -37,6 +37,9 @@ var/sanitized_content = webhook_content sanitized_content = replacetext(sanitized_content, "@everyone", "(Attempted atEveryone)") sanitized_content = replacetext(sanitized_content, "@here", "(Attempted atHere)") + // Fixes speech marks being replaced by invalid chars + // Note that we dont have to care about having to sanitize <> out, because discord handles that + sanitized_content = html_decode(sanitized_content) json["content"] = sanitized_content // Now serialize the embeds diff --git a/code/datums/revision.dm b/code/datums/revision.dm new file mode 100644 index 00000000000..b5eb5d05a79 --- /dev/null +++ b/code/datums/revision.dm @@ -0,0 +1,56 @@ +GLOBAL_DATUM_INIT(revision_info, /datum/code_revision, new) +GLOBAL_PROTECT(revision_info) // Dont mess with this + +/** + * Code Revision Datum + * + * Allows the server code to be aware of the Git environment it is running in, and lets commit hash be viewed + */ +/datum/code_revision + /// Current commit hash the server is running + var/commit_hash + /// Date that this commit was made + var/commit_date + +/datum/code_revision/New() + commit_hash = rustg_git_revparse("HEAD") + if(commit_hash) + commit_date = rustg_git_commit_date(commit_hash) + +/** + * Code Revision Logging Helper + * + * Small proc to simplify logging all this stuff + */ +/datum/code_revision/proc/log_info() + // Put revision info in the world log + var/logmsg + if(commit_hash && commit_date) + logmsg = "Running ParaCode commit: [commit_hash] (Date: [commit_date])" + else + logmsg = "Unable to determine revision info! Code may not be running in a git repository." + + // Log it in all these + log_world(logmsg) + log_runtime_txt(logmsg) + log_runtime_summary(logmsg) + +/client/verb/get_revision_info() + set name = "Get Revision Info" + set category = "OOC" + set desc = "Retrieve technical information about the server" + + var/list/msg = list() + msg += "Server Revision Info" + // Commit info first + if(GLOB.revision_info.commit_hash && GLOB.revision_info.commit_date) + msg += "Server Commit: [GLOB.revision_info.commit_hash] (Date: [GLOB.revision_info.commit_date])" + else + msg += "Server Commit: Unable to determine" + + // Show server BYOND version + msg += "Server BYOND Version: [world.byond_version].[world.byond_build]" + // And the clients for good measure + msg += "Client (your) BYOND Version: [byond_version].[byond_build]" + + to_chat(usr, msg.Join("
")) diff --git a/code/datums/spells/infinite_guns.dm b/code/datums/spells/infinite_guns.dm index 35699d4634a..bb6e30f1bf8 100644 --- a/code/datums/spells/infinite_guns.dm +++ b/code/datums/spells/infinite_guns.dm @@ -6,7 +6,7 @@ range = -1 school = "conjuration" - charge_max = 750 + charge_max = 600 clothes_req = 1 cooldown_min = 10 //Gun wizard action_icon_state = "bolt_action" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index fa175c94c19..b4719690673 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -2,7 +2,6 @@ var/fire = null var/atmosalm = ATMOS_ALARM_NONE var/poweralm = TRUE - var/party = null var/report_alerts = TRUE // Should atmos alerts notify the AI/computers level = null name = "Space" @@ -17,8 +16,6 @@ var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints. var/lightswitch = TRUE - var/eject = null - var/debug = FALSE var/requires_power = TRUE var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() @@ -303,28 +300,6 @@ if(DOOR.density) DOOR.lock() -/area/proc/readyalert() - if(!eject) - eject = 1 - updateicon() - -/area/proc/readyreset() - if(eject) - eject = 0 - updateicon() - -/area/proc/partyalert() - if(!party) - party = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/area/proc/partyreset() - if(party) - party = 0 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - updateicon() - /** * Raise a burglar alert for this area * @@ -375,24 +350,14 @@ L.update() /area/proc/updateicon() - if((eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. - if(!eject && !party) - icon_state = "red" - else if(eject && !party) - icon_state = "red" - else if(party && !eject) - icon_state = "party" - else - icon_state = "blue-red" - else - var/weather_icon - for(var/V in SSweather.processing) - var/datum/weather/W = V - if(W.stage != END_STAGE && (src in W.impacted_areas)) - W.update_areas() - weather_icon = TRUE - if(!weather_icon) - icon_state = null + var/weather_icon + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.stage != END_STAGE && (src in W.impacted_areas)) + W.update_areas() + weather_icon = TRUE + if(!weather_icon) + icon_state = null /area/space/updateicon() icon_state = null diff --git a/code/game/area/ss13_areas.dm b/code/game/area/ss13_areas.dm index b917936151d..2d41414ebfc 100644 --- a/code/game/area/ss13_areas.dm +++ b/code/game/area/ss13_areas.dm @@ -56,12 +56,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/space/firereset(obj/source) return -/area/space/readyalert() - return - -/area/space/partyalert() - return - //These are shuttle areas, they must contain two areas in a subgroup if you want to move a shuttle from one //place to another. Look at escape shuttle for example. //All shuttles show now be under shuttle since we have smooth-wall code. diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index e23e37c349e..b242802c9cf 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -54,6 +54,9 @@ to_chat(owner, "[command]") active_mind_control = TRUE log_admin("[key_name(user)] sent an abductor mind control message to [key_name(owner)]: [command]") + message_admins("[key_name_admin(user)] sent an abductor mind control message to [key_name_admin(owner)]: [command]") + user.create_log(CONVERSION_LOG, "sent an abductor mind control message: '[command]'", owner) + owner.create_log(CONVERSION_LOG, "received an abductor mind control message: '[command]'", user) update_gland_hud() addtimer(CALLBACK(src, .proc/clear_mind_control), mind_control_duration) diff --git a/code/game/gamemodes/miniantags/guardian/types/bomb.dm b/code/game/gamemodes/miniantags/guardian/types/bomb.dm index b69487644f0..bc2e19e6878 100644 --- a/code/game/gamemodes/miniantags/guardian/types/bomb.dm +++ b/code/game/gamemodes/miniantags/guardian/types/bomb.dm @@ -49,6 +49,7 @@ density = A.density appearance = A.appearance dir = A.dir + move_resist = A.move_resist addtimer(CALLBACK(src, .proc/disable), 600) /obj/item/guardian_bomb/proc/disable() diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index 9c76212f082..d324f79d9b7 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -171,7 +171,6 @@ spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns log_name = "IG" category = "Offensive" - cost = 4 //Defensive /datum/spellbook_entry/disabletech diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 6cb53645302..5d123fd225f 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -17,8 +17,10 @@ var/possible_chems = list("ephedrine", "salglu_solution", "salbutamol", "charcoal") var/emergency_chems = list("ephedrine") // Desnowflaking var/amounts = list(5, 10) + /// Beaker loaded into the sleeper. Used for dialysis. var/obj/item/reagent_containers/glass/beaker = null - var/filtering = 0 + /// Whether the machine is currently performing dialysis. + var/filtering = FALSE var/max_chem var/initial_bin_rating = 1 var/min_health = -25 @@ -404,9 +406,9 @@ /obj/machinery/sleeper/proc/toggle_filter() if(filtering || !beaker) - filtering = 0 + filtering = FALSE else - filtering = 1 + filtering = TRUE /obj/machinery/sleeper/proc/go_out() if(filtering) @@ -464,8 +466,8 @@ return if(beaker) - filtering = 0 - beaker.forceMove(usr.loc) + filtering = FALSE + usr.put_in_hands(beaker) beaker = null SStgui.update_uis(src) add_fingerprint(usr) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 98edf130d94..f630fe4e743 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -299,13 +299,12 @@ GLOBAL_LIST_INIT(cloner_biomass_items, list(\ clonemind.transfer_to(H) H.ckey = R.ckey update_clone_antag(H) //Since the body's got the mind, update their antag stuff right now. Otherwise, wait until they get kicked out (as per the CLONER_MATURE_CLONE business) to do it. - to_chat(H, {"Consciousness slowly creeps over you - as your body regenerates.
So this is what cloning - feels like?
"}) + var/message + message += "Consciousness slowly creeps over you as your body regenerates.
" + message += "So this is what cloning feels like?" + to_chat(H, "[message]") else if(grab_ghost_when == CLONER_MATURE_CLONE) - to_chat(clonemind.current, {"Your body is - beginning to regenerate in a cloning pod. You will - become conscious when it is complete."}) + to_chat(clonemind.current, "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") // Set up a soul link with the dead body to catch a revival soullink(/datum/soullink/soulhook, clonemind.current, src) @@ -498,6 +497,7 @@ GLOBAL_LIST_INIT(cloner_biomass_items, list(\ clonemind.transfer_to(occupant) occupant.grab_ghost() update_clone_antag(occupant) + to_chat(occupant, "You remember nothing from the time that you were dead!") to_chat(occupant, "There is a bright flash!
\ You feel like a new being.
") occupant.flash_eyes(visual = 1) @@ -533,9 +533,10 @@ GLOBAL_LIST_INIT(cloner_biomass_items, list(\ if(occupant.mind != clonemind) clonemind.transfer_to(occupant) occupant.grab_ghost() // We really just want to make you suffer. - to_chat(occupant, {"Agony blazes across your - consciousness as your body is torn apart.
- Is this what dying is like? Yes it is.
"}) + var/message + message += "Agony blazes across your consciousness as your body is torn apart.
" + message += "Is this what dying is like? Yes it is." + to_chat(occupant, "[message]") occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) for(var/i in missing_organs) qdel(i) diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 2ba3b25b6f6..5933fc1b7d0 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -205,7 +205,6 @@ GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0) if("ASSAULT3") spawn(0) M.close() - special_ops.readyreset()//Reset firealarm after the team launched. //End Marauder launchpad. var/area/start_location = locate(/area/shuttle/specops/centcom) @@ -325,9 +324,6 @@ GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0) temp += "Shuttle departing.

OK" updateUsrDialog() - var/area/centcom/specops/special_ops = locate() - if(special_ops) - special_ops.readyalert()//Trigger alarm for the spec ops area. GLOB.specops_shuttle_moving_to_station = 1 GLOB.specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index f25c95ec42f..efe5617511e 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -239,9 +239,6 @@ GLOBAL_VAR_INIT(syndicate_elite_shuttle_timeleft, 0) temp = "Shuttle departing.

OK" updateUsrDialog() - var/area/syndicate_mothership/elite_squad/elite_squad = locate() - if(elite_squad) - elite_squad.readyalert()//Trigger alarm for the spec ops area. GLOB.syndicate_elite_shuttle_moving_to_station = 1 GLOB.syndicate_elite_shuttle_time = world.timeofday + SYNDICATE_ELITE_MOVETIME diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 40e462d2212..7e58cb992a7 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -572,7 +572,7 @@ willing = 1 if(willing) - if(!Adjacent(L)) + if(!Adjacent(L) && !Adjacent(user)) to_chat(user, "You're not close enough to [src].") return if(L == user) diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 08e2f253d3f..b4214fe3b23 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -300,105 +300,6 @@ Just a object used in constructing fire alarms toolspeed = 1 usesound = 'sound/items/deconstruct.ogg' -/obj/machinery/partyalarm - name = "\improper PARTY BUTTON" - desc = "Cuban Pete is in the house!" - icon = 'icons/obj/monitors.dmi' - icon_state = "fire0" - var/detecting = 1.0 - var/working = 1.0 - var/time = 10.0 - var/timing = 0.0 - var/lockdownbyai = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 6 - -/obj/machinery/partyalarm/attack_hand(mob/user) - if((user.stat && !isobserver(user)) || stat & (NOPOWER|BROKEN)) - return - - user.machine = src - var/area/A = get_area(src) - ASSERT(isarea(A)) - var/d1 - var/d2 - if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) - - if(A.party) - d1 = "No Party :(" - else - d1 = "PARTY!!!" - if(timing) - d2 = "Stop Time Lock" - else - d2 = "Initiate Time Lock" - var/second = time % 60 - var/minute = (time - second) / 60 - var/dat = text("Party Button []\n
\nTimer System: []
\nTime Left: [][] - - + +\n
", d1, d2, (minute ? text("[]:", minute) : null), second) - user << browse(dat, "window=partyalarm") - onclose(user, "partyalarm") - else - if(A.fire) - d1 = text("[]", stars("No Party :(")) - else - d1 = text("[]", stars("PARTY!!!")) - if(timing) - d2 = text("[]", stars("Stop Time Lock")) - else - d2 = text("[]", stars("Initiate Time Lock")) - var/second = time % 60 - var/minute = (time - second) / 60 - var/dat = text("[] []\n
\nTimer System: []
\nTime Left: [][] - - + +\n
", stars("Party Button"), d1, d2, (minute ? text("[]:", minute) : null), second) - user << browse(dat, "window=partyalarm") - onclose(user, "partyalarm") - return - -/obj/machinery/partyalarm/proc/reset() - if(!( working )) - return - var/area/A = get_area(src) - ASSERT(isarea(A)) - A.partyreset() - return - -/obj/machinery/partyalarm/proc/alarm() - if(!( working )) - return - var/area/A = get_area(src) - ASSERT(isarea(A)) - A.partyalert() - return - -/obj/machinery/partyalarm/Topic(href, href_list) - ..() - if(usr.stat || stat & (BROKEN|NOPOWER)) - return - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - usr.machine = src - if(href_list["reset"]) - reset() - else - if(href_list["alarm"]) - alarm() - else - if(href_list["time"]) - timing = text2num(href_list["time"]) - else - if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - time += tp - time = min(max(round(time), 0), 120) - updateUsrDialog() - - add_fingerprint(usr) - else - usr << browse(null, "window=partyalarm") - return - return - - #undef FIRE_ALARM_FRAME #undef FIRE_ALARM_UNWIRED #undef FIRE_ALARM_READY diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 4564fb36fab..0f4c8a913ba 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -126,6 +126,8 @@ Class Procs: var/frequency = NONE /// A reference to a `datum/radio_frequency`. Gives the machine the ability to interact with things using radio signals. var/datum/radio_frequency/radio_connection + /// This is if the machinery is being repaired + var/being_repaired = FALSE /* * reimp, attempts to flicker this machinery if the behavior is supported. @@ -476,6 +478,34 @@ Class Procs: if(.) power_change() +/obj/machinery/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/stack/nanopaste)) + var/obj/item/stack/nanopaste/N = O + if(stat & BROKEN) + to_chat(user, "[src] is too damaged to be fixed with nanopaste!") + return + if(obj_integrity == max_integrity) + to_chat(user, "[src] is fully intact.") + return + if(being_repaired) + return + if(N.get_amount() < 1) + to_chat(user, "You don't have enough to complete this task!") + return + to_chat(user, "You start applying [O] to [src].") + being_repaired = TRUE + var/result = do_after(user, 3 SECONDS, target = src) + being_repaired = FALSE + if(!result) + return + if(!N.use(1)) + to_chat(user, "You don't have enough to complete this task!") // this is here, as we don't want to use nanopaste until you finish applying + return + obj_integrity = min(obj_integrity + 50, max_integrity) + user.visible_message("[user] applied some [O] at [src]'s damaged areas.",\ + "You apply some [O] at [src]'s damaged areas.") + else + return ..() /obj/machinery/proc/exchange_parts(mob/user, obj/item/storage/part_replacer/W) var/shouldplaysound = 0 if((flags & NODECONSTRUCT)) diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm index 0adbfd5b693..677fe5e7c25 100644 --- a/code/game/machinery/slotmachine.dm +++ b/code/game/machinery/slotmachine.dm @@ -47,9 +47,9 @@ if(action == "spin") if(working) return - if(!account || account.money < 10) + if(!account || account.money < 50) return - if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine")) + if(!account.charge(50, null, "Bet", "Slot Machine", "Slot Machine")) return plays++ working = TRUE @@ -58,45 +58,34 @@ addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25) /obj/machinery/slot_machine/proc/spin_slots(userName) - switch(rand(1,4050)) - if(1) // .02% - atom_say("JACKPOT! [userName] has won a MILLION CREDITS!") - GLOB.event_announcement.Announce("Congratulations to [userName] on winning the Jackpot of ONE MILLION CREDITS!", "Jackpot Winner") - result = "JACKPOT! You win one million credits!" + switch(rand(1,5000)) + if(1) + atom_say("JACKPOT! [userName] has won ten thousand credits!") + GLOB.event_announcement.Announce("Congratulations to [userName] on winning the Jackpot of TEN THOUSAND CREDITS!", "Jackpot Winner") + result = "JACKPOT! You win ten thousand credits!" resultlvl = "teal" - win_money(1000000, 'sound/goonstation/misc/airraid_loop.ogg') - if(2 to 5) // .07% - atom_say("Big Winner! [userName] has won a hundred thousand credits!") - GLOB.event_announcement.Announce("Congratulations to [userName] on winning a hundred thousand credits!", "Big Winner") - result = "Big Winner! You win a hundred thousand credits!" - resultlvl = "green" - win_money(100000, 'sound/goonstation/misc/klaxon.ogg') - if(6 to 50) // 1.08% - atom_say("Big Winner! [userName] has won ten thousand credits!") - result = "You win ten thousand credits!" - resultlvl = "green" - win_money(10000, 'sound/goonstation/misc/klaxon.ogg') - if(51 to 100) // 1.21% - atom_say("Winner! [userName] has won a thousand credits!") + win_money(10000, 'sound/goonstation/misc/airraid_loop.ogg') + if(2 to 20) + atom_say("Big Winner! [userName] has won a thousand credits!") result = "You win a thousand credits!" resultlvl = "green" - win_money(1000, 'sound/goonstation/misc/bell.ogg') - if(101 to 200) // 2.44% - atom_say("Winner! [userName] has won a hundred credits!") - result = "You win a hundred credits!" + win_money(1000, 'sound/goonstation/misc/klaxon.ogg') + if(21 to 100) + atom_say("Winner! [userName] has won five hundred credits!") + result = "You win five hundred credits!" resultlvl = "green" - win_money(100, 'sound/goonstation/misc/bell.ogg') - if(201 to 300) // 2.44% + win_money(500, 'sound/goonstation/misc/bell.ogg') + if(101 to 500) + atom_say("Winner! [userName] has won two hundred credits!") + result = "You win two hundred credits!" + resultlvl = "green" + win_money(200) + if(501 to 1000) atom_say("Winner! [userName] has won fifty credits!") result = "You win fifty credits!" resultlvl = "green" win_money(50) - if(301 to 1000) // 17.26% - atom_say("Winner! [userName] has won ten credits!") - result = "You win ten credits!" - resultlvl = "green" - win_money(10) - else // 75.31% + else result = "No luck!" resultlvl = "orange" working = FALSE diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 7e0101b891e..d01f8fd856b 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -840,6 +840,9 @@ if(repairing) to_chat(user, "[src] is currently being repaired!") return + if(state == 0) // If maint protocols are not active, the state is zero + to_chat(user, "[src] can not be repaired without maintenance protocols active!") + return WELDER_ATTEMPT_REPAIR_MESSAGE repairing = TRUE if(I.use_tool(src, user, 15, volume = I.tool_volume)) diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm deleted file mode 100644 index 3f8eb81c6b9..00000000000 --- a/code/game/objects/effects/effect_system.dm +++ /dev/null @@ -1,1289 +0,0 @@ -/* This is an attempt to make some easily reusable "particle" type effect, to stop the code -constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one -defined, then set up when it is created with New(). Then this same system can just be reused each time -it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam -would spawn and follow the beaker, even if it is carried or thrown. -*/ - - -/obj/effect/effect - name = "effect" - icon = 'icons/effects/effects.dmi' - mouse_opacity = 0 - unacidable = 1//So effect are not targeted by alien acid. - -/obj/effect/effect/New() - ..() - if(ticker) - cameranet.updateVisibility(src) - -/obj/effect/effect/Destroy() - if(ticker) - cameranet.updateVisibility(src) - return ..() - -/datum/effect/proc/fadeOut(var/atom/A, var/frames = 16) - if(A.alpha == 0) //Handle already transparent case - return - if(frames == 0) - frames = 1 //We will just assume that by 0 frames, the coder meant "during one frame". - var/step = A.alpha / frames - for(var/i = 0, i < frames, i++) - A.alpha -= step - sleep(world.tick_lag) - return - -/obj/effect/effect/water - name = "water" - icon = 'icons/effects/effects.dmi' - icon_state = "extinguish" - var/life = 15.0 - mouse_opacity = 0 - -/obj/effect/effect/smoke - name = "smoke" - icon = 'icons/effects/water.dmi' - icon_state = "smoke" - opacity = 1 - anchored = 0.0 - mouse_opacity = 0 - var/amount = 8.0 - -/obj/effect/proc/delete() - qdel(src) - - -/obj/effect/effect/water/New() - ..() - //var/turf/T = src.loc - //if(istype(T, /turf)) - // T.firelevel = 0 //TODO: FIX - spawn( 70 ) - delete() - return - return - -/obj/effect/effect/water/Move(turf/newloc) - //var/turf/T = src.loc - //if(istype(T, /turf)) - // T.firelevel = 0 //TODO: FIX - if(--src.life < 1) - //SN src = null - delete() - if(newloc.density) - return 0 - .=..() - -/obj/effect/effect/water/Bump(atom/A) - if(reagents) - reagents.reaction(A) - if(istype(A,/atom/movable)) - var/atom/movable/AM = A - AM.water_act(life, COLD_WATER_TEMPERATURE, src) - return ..() - - -/datum/effect/system - var/number = 3 - var/cardinals = 0 - var/turf/location - var/atom/holder - var/setup = 0 - - Destroy() - holder = null - location = null - return ..() - - proc/set_up(n = 3, c = 0, turf/loc) - if(n > 10) - n = 10 - number = n - cardinals = c - location = loc - setup = 1 - - proc/attach(atom/atom) - holder = atom - - proc/start() - - -///////////////////////////////////////////// -// GENERIC STEAM SPREAD SYSTEM - -//Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location) -// The attach(atom/atom) proc is optional, and can be called to attach the effect -// to something, like a smoking beaker, so then you can just call start() and the steam -// will always spawn at the items location, even if it's moved. - -/* Example: - var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() -- creates new system - steam.set_up(5, 0, mob.loc) -- sets up variables - OPTIONAL: steam.attach(mob) - steam.start() -- spawns the effect -*/ -///////////////////////////////////////////// -/obj/effect/effect/steam - name = "steam" - icon = 'icons/effects/effects.dmi' - icon_state = "extinguish" - density = 0 - -/datum/effect/system/steam_spread - - set_up(n = 3, c = 0, turf/loc) - if(n > 10) - n = 10 - number = n - cardinals = c - location = loc - - start() - var/i = 0 - for(i=0, i 10) - n = 10 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - - start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/sparks/sparks = new /obj/effect/effect/sparks(src.location) - src.total_sparks++ - var/direction - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i 10) - n = 10 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - if(direct) - direction = direct - - - start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/harmless_smoke/smoke = new /obj/effect/effect/harmless_smoke(src.location) - src.total_smoke++ - var/direction = src.direction - if(!direction) - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i 20) - n = 20 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - if(direct) - direction = direct - - start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/bad_smoke/smoke = new /obj/effect/effect/bad_smoke(src.location) - src.total_smoke++ - var/direction = src.direction - if(!direction) - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i 20) - n = 20 - number = n - cardinals = c - carry.copy_to(chemholder, carry.total_volume) - - - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - if(direct) - direction = direct - if(!silent) - var/contained = "" - for(var/reagent in carry.reagent_list) - contained += " [reagent] " - if(contained) - contained = "\[[contained]\]" - var/area/A = get_area(location) - - var/where = "[A.name] | [location.x], [location.y]" - var/whereLink = "[where]" - - if(carry && carry.my_atom) - if(carry.my_atom.fingerprintslast) - var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast) - var/more = "" - if(M) - more = " " - add_attack_logs(M, location, "Caused a chemical smoke reaction containing [contained]. Last associated key is [carry.my_atom.fingerprintslast][more]", ATKLOG_FEW) - log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].") - else - msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1) - log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.") - else - msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink]). No associated key. CODERS: carry.my_atom may be null.", 0, 1) - log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key. CODERS: carry.my_atom may be null.") - - start(effect_range = 2) - var/i = 0 - - var/color = mix_color_from_reagents(chemholder.reagents.reagent_list) - var/obj/effect/effect/chem_smoke/smokeholder = new /obj/effect/effect/chem_smoke(src.location) - for(var/atom/A in view(effect_range, smokeholder)) - chemholder.reagents.reaction(A) - if(iscarbon(A)) - var/mob/living/carbon/C = A - if(C.can_breathe_gas()) - chemholder.reagents.copy_to(C, chemholder.reagents.total_volume) - qdel(smokeholder) - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/chem_smoke/smoke = new /obj/effect/effect/chem_smoke(src.location) - src.total_smoke++ - var/direction = src.direction - if(!direction) - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - - if(color) - smoke.icon += color // give the smoke color, if it has any to begin with - else - // if no color, just use the old smoke icon - smoke.icon = 'icons/effects/96x96.dmi' - smoke.icon_state = "smoke" - for(i=0, i 20) - n = 20 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - if(direct) - direction = direct - - - start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/sleep_smoke/smoke = new /obj/effect/effect/sleep_smoke(src.location) - src.total_smoke++ - var/direction = src.direction - if(!direction) - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i 20) - n = 20 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - if(direct) - direction = direct - - start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/mustard_gas/smoke = new /obj/effect/effect/mustard_gas(src.location) - src.total_smoke++ - var/direction = src.direction - if(!direction) - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i[user] smashes through \the [src].", "You smash through \the [src].") - qdel(src) - else - to_chat(user, "You hit the metal foam but bounce off it.") - -/obj/structure/foamedmetal/attackby(var/obj/item/I, var/mob/user, params) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src) - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - G.affecting.loc = src.loc - user.visible_message("[G.assailant] smashes [G.affecting] through the foamed metal wall.") - qdel(I) - qdel(src) - return - - if(prob(I.force*20 - metal*25)) - user.visible_message("[user] smashes through the foamed metal with \the [I].", "You smash through the foamed metal with \the [I].") - qdel(src) - else - to_chat(user, "You hit the metal foam to no effect.") - -/obj/structure/foamedmetal/attack_animal(mob/living/simple_animal/M) - M.do_attack_animation(src) - if(M.melee_damage_upper == 0) - M.visible_message("[M] nudges \the [src].") - else - if(M.attack_sound) - playsound(loc, M.attack_sound, 50, 1, 1) - M.visible_message("\The [M] [M.attacktext] [src]!") - qdel(src) - -/obj/structure/foamedmetal/attack_alien(mob/living/carbon/alien/humanoid/M) - M.visible_message("[M] tears apart \the [src]!") - qdel(src) - -/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target, height=1.5) - return !density - -/obj/structure/foamedmetal/CanAtmosPass() - return !density - -/datum/effect/system/reagents_explosion - var/amount // TNT equivalent - var/flashing = 0 // does explosion creates flash effect? - var/flashing_factor = 0 // factor of how powerful the flash effect relatively to the explosion - - set_up (amt, loc, flash = 0, flash_fact = 0) - amount = amt - if(istype(loc, /turf/)) - location = loc - else - location = get_turf(loc) - - flashing = flash - flashing_factor = flash_fact - - return - - start() - if(amount <= 2) - var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread - s.set_up(2, 1, location) - s.start() - - for(var/mob/M in viewers(5, location)) - to_chat(M, "The solution violently explodes.") - for(var/mob/M in viewers(1, location)) - if(prob (50 * amount)) - to_chat(M, "The explosion knocks you down.") - M.Weaken(rand(1,5)) - return - else - var/devastation = -1 - var/heavy = -1 - var/light = -1 - var/flash = -1 - - // Clamp all values to MAX_EXPLOSION_RANGE - if(round(amount/12) > 0) - devastation = min (MAX_EX_DEVASTATION_RANGE, devastation + round(amount/12)) - - if(round(amount/6) > 0) - heavy = min (MAX_EX_HEAVY_RANGE, heavy + round(amount/6)) - - if(round(amount/3) > 0) - light = min (MAX_EX_LIGHT_RANGE, light + round(amount/3)) - - if(flash && flashing_factor) - flash += (round(amount/4) * flashing_factor) - - for(var/mob/M in viewers(8, location)) - to_chat(M, "The solution violently explodes.") - - explosion(location, devastation, heavy, light, flash) - - proc/holder_damage(var/atom/holder) - if(holder) - var/dmglevel = 4 - - if(round(amount/8) > 0) - dmglevel = 1 - else if(round(amount/4) > 0) - dmglevel = 2 - else if(round(amount/2) > 0) - dmglevel = 3 - - if(dmglevel<4) holder.ex_act(dmglevel) - - -////////////////////////////////// -//////SPARKLE FIREWORKS -///////////////////////////////// -//////////////////////////// -/obj/effect/sparkles - name = "sparkle" - icon = 'icons/obj/fireworks.dmi'//findback - icon_state = "sparkel" - var/amount = 6.0 - anchored = 1.0 - mouse_opacity = 0 - -/obj/effect/sparkles/New() - ..() - var/icon/I = new(src.icon,src.icon_state) - var/r = rand(0,255) - var/g = rand(0,255) - var/b = rand(0,255) - I.Blend(rgb(r,g,b),ICON_MULTIPLY) - src.icon = I - playsound(src.loc, "sparks", 100, 1) - var/turf/T = src.loc - if(istype(T, /turf)) - T.hotspot_expose(3000,100) - spawn (100) - qdel(src) - return - -/obj/effect/sparkles/Destroy() - var/turf/T = src.loc - if(istype(T, /turf)) - T.hotspot_expose(3000,100) - return ..() - -/obj/effect/sparkles/Move() - ..() - var/turf/T = src.loc - if(istype(T, /turf)) - T.hotspot_expose(3000,100) - return - - -/datum/effect/system/sparkle_spread - var/total_sparks = 0 // To stop it being spammed and lagging! - -/datum/effect/system/sparkle_spread/set_up(n = 3, c = 0, loca) - if(n > 10) - n = 10 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - -/datum/effect/system/sparkle_spread/start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/sparkles/sparks = new(src.location) - src.total_sparks++ - var/direction - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i= affecting_level) AM.ex_act(dist) else for(var/atom in T.contents) //see above var/atom/AM = atom - if(AM && AM.simulated) + if(!QDELETED(AM) && AM.simulated) AM.ex_act(dist) CHECK_TICK if(breach) diff --git a/code/game/objects/items/devices/pipe_painter.dm b/code/game/objects/items/devices/pipe_painter.dm index c386e64a19d..5fb3f3dc597 100644 --- a/code/game/objects/items/devices/pipe_painter.dm +++ b/code/game/objects/items/devices/pipe_painter.dm @@ -33,7 +33,7 @@ /obj/item/pipe_painter/attack_self(mob/user as mob) - mode = input("Which colour do you want to use?", "Pipe Painter", mode) in modes + mode = input("Which colour do you want to use?", name, mode) in modes /obj/item/pipe_painter/examine(mob/user) . = ..() diff --git a/code/game/objects/items/devices/window_painter.dm b/code/game/objects/items/devices/window_painter.dm new file mode 100644 index 00000000000..ae7d379cfd4 --- /dev/null +++ b/code/game/objects/items/devices/window_painter.dm @@ -0,0 +1,22 @@ +/obj/item/pipe_painter/window_painter + name = "window painter" + icon_state = "window_painter" + + var/list/paintable_windows = list( + /obj/structure/window/reinforced, + /obj/structure/window/basic, + /obj/structure/window/full/reinforced, + /obj/structure/window/full/basic, + /obj/machinery/door/window) + +/obj/item/pipe_painter/window_painter/afterattack(atom/A, mob/user as mob) + if(!is_type_in_list(A, paintable_windows) || !in_range(user, A)) + return + var/obj/structure/window/W = A + + if(W.color == GLOB.pipe_colors[mode]) + to_chat(user, "This window is aready painted [mode]!") + return + + playsound(loc, usesound, 30, TRUE) + W.color = GLOB.pipe_colors[mode] diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index edf32e2f5a6..1f7304a7128 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -5,20 +5,16 @@ w_class = WEIGHT_CLASS_BULKY item_state = "jetpack" distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - var/datum/effect_system/trail_follow/ion/ion_trail actions_types = list(/datum/action/item_action/set_internals, /datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) var/on = 0 var/stabilizers = 0 var/volume_rate = 500 //Needed for borg jetpack transfer -/obj/item/tank/jetpack/New() - ..() - ion_trail = new /datum/effect_system/trail_follow/ion() - ion_trail.set_up(src) - -/obj/item/tank/jetpack/Destroy() - QDEL_NULL(ion_trail) - return ..() +/obj/item/tank/jetpack/on_mob_move(direction, mob/user) + if(on) + var/turf/T = get_step(src, GetOppositeDir(direction)) + if(!has_gravity(T)) + new /obj/effect/particle_effect/ion_trails(T, direction) /obj/item/tank/jetpack/ui_action_click(mob/user, actiontype) if(actiontype == /datum/action/item_action/toggle_jetpack) @@ -59,14 +55,11 @@ /obj/item/tank/jetpack/proc/turn_on(mob/user) on = TRUE icon_state = "[initial(icon_state)]-on" - ion_trail.start() /obj/item/tank/jetpack/proc/turn_off(mob/user) on = FALSE stabilizers = FALSE icon_state = initial(icon_state) - ion_trail.stop() - /obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user) if(!on) @@ -150,8 +143,6 @@ /obj/item/tank/jetpack/carbondioxide/New() ..() - ion_trail = new /datum/effect_system/trail_follow/ion() - ion_trail.set_up(src) air_contents.carbon_dioxide = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) /obj/item/tank/jetpack/carbondioxide/examine(mob/user) diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index e45d13e2270..35daeff58e9 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -72,24 +72,35 @@ /obj/structure/closet/jcloset/New() ..() - new /obj/item/clothing/under/rank/janitor(src) - new /obj/item/radio/headset/headset_service(src) - new /obj/item/cartridge/janitor(src) + new /obj/item/flashlight(src) new /obj/item/flashlight(src) new /obj/item/melee/flyswatter(src) + new /obj/item/melee/flyswatter(src) + new /obj/item/clothing/shoes/galoshes(src) new /obj/item/clothing/shoes/galoshes(src) new /obj/item/soap(src) + new /obj/item/soap(src) + new /obj/item/reagent_containers/spray/cleaner(src) new /obj/item/caution(src) new /obj/item/caution(src) new /obj/item/caution(src) new /obj/item/caution(src) new /obj/item/storage/bag/trash(src) + new /obj/item/storage/bag/trash(src) + new /obj/item/lightreplacer(src) new /obj/item/lightreplacer(src) new /obj/item/holosign_creator(src) - new /obj/item/clothing/gloves/color/black(src) - new /obj/item/clothing/head/soft/purple(src) + new /obj/item/holosign_creator(src) + new /obj/item/watertank/janitor(src) new /obj/item/watertank/janitor(src) new /obj/item/storage/belt/janitor(src) + new /obj/item/storage/belt/janitor(src) + new /obj/item/clothing/gloves/color/black(src) + new /obj/item/clothing/head/soft/purple(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/clothing/under/rank/janitor(src) + new /obj/item/cartridge/janitor(src) /* * Lawyer diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 62ccc187ecb..0e75bb760f9 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -50,6 +50,7 @@ name = "grass" icon = 'icons/obj/flora/snowflora.dmi' anchored = 1 + max_integrity = 15 /obj/structure/flora/grass/brown icon_state = "snowgrass1bb" @@ -80,6 +81,7 @@ icon = 'icons/obj/flora/snowflora.dmi' icon_state = "snowbush1" anchored = 1 + max_integrity = 15 /obj/structure/flora/bush/New() ..() @@ -92,6 +94,7 @@ icon = 'icons/obj/flora/ausflora.dmi' icon_state = "firstbush_1" anchored = 1 + max_integrity = 15 /obj/structure/flora/ausbushes/New() ..() diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 3e68456e42c..307dd1b7da2 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -71,6 +71,10 @@ base = "rtable" if(istype(src, /obj/structure/table/wood/poker)) base = "poker" + if(istype(src, /obj/structure/table/wood/fancy)) + base = "fancy" + if(istype(src, /obj/structure/table/wood/fancy/black)) + base = "fancyblack" icon_state = "[base]flip[type][type == 1 ? subtype : ""]" diff --git a/code/game/world.dm b/code/game/world.dm index a25ba837cc3..bb19ac745cc 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -10,6 +10,7 @@ GLOBAL_LIST_INIT(map_transition_config, MAP_TRANSITION_CONFIG) TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED) // creates a new TGS object log_world("World loaded at [time_stamp()]") log_world("[GLOB.vars.len - GLOB.gvars_datum_in_built_vars.len] global variables") + GLOB.revision_info.log_info() connectDB() // This NEEDS TO HAPPEN EARLY. I CANNOT STRESS THIS ENOUGH!!!!!!! -aa load_admins() // Same here diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index ab9e215fbba..2d338825f22 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -91,7 +91,10 @@ GLOBAL_VAR_INIT(nologevent, 0) else body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]" - body += "
BYOND account registration date: [M.client.byondacc_date || "ERROR"] [M.client.byondacc_age <= config.byond_account_age_threshold ? "" : ""]([M.client.byondacc_age] days old)[M.client.byondacc_age <= config.byond_account_age_threshold ? "" : ""]
" + body += "
BYOND account registration date: [M.client.byondacc_date || "ERROR"] [M.client.byondacc_age <= config.byond_account_age_threshold ? "" : ""]([M.client.byondacc_age] days old)[M.client.byondacc_age <= config.byond_account_age_threshold ? "" : ""]" + body += "
Global Ban DB Lookup: [config.centcom_ban_db_url ? "Lookup" : "Disabled"]" + + body += "
" if(isnewplayer(M)) body += " Hasn't Entered Game " diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7309abf6c6b..723b6a01d11 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -69,7 +69,8 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/reset_all_tcs, /*resets all telecomms scripts*/ /client/proc/toggle_mentor_chat, /client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/ - /client/proc/list_ssds_afks + /client/proc/list_ssds_afks, + /client/proc/ccbdb_lookup_ckey )) GLOBAL_LIST_INIT(admin_verbs_ban, list( /client/proc/ban_panel, diff --git a/code/modules/admin/centcom_ban_db.dm b/code/modules/admin/centcom_ban_db.dm new file mode 100644 index 00000000000..6416a1a935b --- /dev/null +++ b/code/modules/admin/centcom_ban_db.dm @@ -0,0 +1,117 @@ +// CCBDB = CentCom Ban DB +// This file contains procs for handling all requests to the CCBDB API, hosted by Bobbahbrown +// API Documentation: https://centcom.melonmesa.com/swagger/index.html +// Please refer to said documentation before editing any of the stuff in here, otherwise it will likely break + + +/** + * CCBDB Lookup Initiator + * + * Checks the configuration before invoking the request to the CCBDB server. + * + * Arguments: + * * ckey - ckey to be looked up + */ +/datum/admins/proc/create_ccbdb_lookup(ckey) + // Bail if disabled + if(!config.centcom_ban_db_url) + to_chat(usr, "The CentCom Ban DB lookup is disabled. Please inform a maintainer or server host.") + return + // Bail if no ckey is supplied + if(!ckey) + return + + var/datum/callback/cb = CALLBACK(src, /datum/admins/.proc/ccbdb_lookup_callback, usr, ckey) + SShttp.create_async_request(RUSTG_HTTP_METHOD_GET, "[config.centcom_ban_db_url][ckey]", proc_callback=cb) + +/** + * CCBDB Lookup Callback + * + * Callback assigned in [/datum/admins/proc/create_ccbdb_lookup] for async operations without a sleep() + * + * Arguments: + * * user - Mob calling the lookup so the UI can be opened + * * ckey - Ckey being looked up + * * response - [/datum/http_response] passed through from [SShttp] + */ +/datum/admins/proc/ccbdb_lookup_callback(mob/user, ckey, datum/http_response/response) + // If the admin DC'd during the lookup, dont try and do things + if(!user) + return + + // Bail if it errored + if(response.errored) + to_chat(user, "Error connecting to CentCom Ban DB. Please inform a maintainer or server host.") + return + + // Bail if the code isnt 200 + if(response.status_code != 200) + to_chat(user, "Error performing CentCom Ban DB lookup (Code: [response.status_code])") + return + + var/list/popup_data = list() + + // A body of "[]" means there were no bans on record for the user + if(response.body == "\[]") + popup_data += "

0 bans detected for [ckey]

" + else + var/list/bans = list() + // Wrap this in try/catch because JSON is a finnicky bastard + try + bans = json_decode(response.body) + // Yes I threw a ternary in because I am that obsessed over OCD + popup_data += "

[length(bans)] ban[length(bans) == 1 ? "" : "s"] detected for [ckey]

" + // These vars are also just for OCD (Adding a
between each ban but the last) + var/total_bans = length(bans) + var/index = 0 + for(var/list/ban in bans) + index++ + // For anyone else who edits this. SANITIZE ALL THE DATA. OTHER SERVERS MAY HAVE BAD ACTORS WHO TRY AND XSS PEOPLE. + popup_data += "Server: [sanitize(ban["sourceName"])] ([sanitize(ban["sourceRoleplayLevel"])] RP)
" + + var/ban_status + // If the ban is active, its, well, active + if(ban["active"] == TRUE) // The == TRUE is intentional here. Dont remove it. + ban_status = "Ban Active" + // If they have an entry of being unbanned by someone, its been appealed + else if(ban["unbannedBy"]) + ban_status = "Ban Appealed" + else + ban_status = "Ban Expired" + popup_data += "Status: [ban_status]
" + + // All the other stats + popup_data += "Ban Type: [sanitize(ban["type"])]
" + popup_data += "Banning Admin: [sanitize(ban["bannedBy"])]
" + popup_data += "Ban Date: [sanitize(ban["bannedOn"])]
" + var/expiration = ban["expires"] + popup_data += "Expires: [expiration ? "[sanitize(expiration)]" : "Permanent"]
" + popup_data += "Ban Reason: [sanitize(ban["reason"])]
" + + // If its a job ban, tell the admin the job list + if(ban["type"] == "Job") + var/list/jobs = ban["jobs"] + popup_data += "Jobs: [sanitize(english_list(jobs))]
" + + // Add a newline between bans if its not the last one + if(index != total_bans) + popup_data += "
" + + catch + to_chat(user, "Error parsing JSON data from CentCom Ban DB lookup. Please inform a maintainer.") + return + + var/datum/browser/popup = new(user, "ccbdblookup-[ckey]", "
CC Ban DB Lookup - [ckey]
", 700, 600) + popup.set_content(popup_data.Join()) + popup.open(FALSE) + +// Just a simple verb so admins can do manual lookups +/client/proc/ccbdb_lookup_ckey() + set name = "Global Ban DB Lookup" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + var/input_ckey = input(usr, "Please enter a ckey to lookup", "Global Ban DB Lookup") + holder.create_ccbdb_lookup(input_ckey) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index ed961e83337..cae09b224d2 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -3349,21 +3349,6 @@ // Refresh the page src.view_flagged_books() - // Force unlink a discord key - // TODO: Delete this - else if(href_list["force_discord_unlink"]) - if(!check_rights(R_ADMIN)) - return - var/target_ckey = href_list["force_discord_unlink"] - var/DBQuery/admin_unlink_discord_id = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("discord")] WHERE ckey = '[target_ckey]'") - if(!admin_unlink_discord_id.Execute()) - var/err = admin_unlink_discord_id.ErrorMsg() - log_game("SQL ERROR while admin-unlinking discord account. Error : \[[err]\]\n") - return - to_chat(src, "Successfully forcefully unlinked discord account from [target_ckey]") - message_admins("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") - log_admin("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") - else if(href_list["create_outfit_finalize"]) if(!check_rights(R_EVENT)) return @@ -3386,6 +3371,11 @@ return var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits save_outfit(usr,O) + else if(href_list["open_ccbdb"]) + if(!check_rights(R_ADMIN)) + return + create_ccbdb_lookup(href_list["open_ccbdb"]) + /client/proc/create_eventmob_for(var/mob/living/carbon/human/H, var/killthem = 0) if(!check_rights(R_EVENT)) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 324899028fc..c5fbe05a5e6 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -45,7 +45,7 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo var/active_admins = admincount[1] log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [active_admins] non-AFK admins.") - SSdiscord.send2discord_simple_noadmins("[selected_type] from [key_name(src)]: [msg]", check_send_always = TRUE) + SSdiscord.send2discord_simple_noadmins("**\[Adminhelp]** [key_name(src)]: [msg]", check_send_always = TRUE) if("Mentorhelp") var/alerttext diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 2bc4110181b..289aa3ec68f 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -754,7 +754,7 @@ if(!cidcheck_failedckeys[ckey]) message_admins("[key_name(src)] has been detected as using a CID randomizer. Connection rejected.") - SSdiscord.send2discord_simple_noadmins("[key_name(src)] has been detected as using a CID randomizer. Connection rejected.") + SSdiscord.send2discord_simple_noadmins("**\[Warning]** [key_name(src)] has been detected as using a CID randomizer. Connection rejected.") cidcheck_failedckeys[ckey] = TRUE note_randomizer_user() @@ -767,7 +767,7 @@ if(cidcheck_failedckeys[ckey]) // Atonement message_admins("[key_name_admin(src)] has been allowed to connect after showing they removed their cid randomizer") - SSdiscord.send2discord_simple_noadmins("[key_name(src)] has been allowed to connect after showing they removed their cid randomizer.") + SSdiscord.send2discord_simple_noadmins("**\[Info]** [key_name(src)] has been allowed to connect after showing they removed their cid randomizer.") cidcheck_failedckeys -= ckey if (cidcheck_spoofckeys[ckey]) message_admins("[key_name_admin(src)] has been allowed to connect after appearing to have attempted to spoof a cid randomizer check because it appears they aren't spoofing one this time") diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index c4a01518922..4a6fe48753d 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -293,7 +293,7 @@ /client/verb/toggle_ghost_pda() set name = "Show/Hide GhostPDA" set category = "Preferences" - set desc = ".Toggle seeing PDA messages as an observer." + set desc = "Toggle seeing PDA messages as an observer." prefs.toggles ^= PREFTOGGLE_CHAT_GHOSTPDA to_chat(src, "As a ghost, you will now [(prefs.toggles & PREFTOGGLE_CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].") prefs.save_preferences(src) @@ -314,3 +314,11 @@ prefs.toggles2 ^= PREFTOGGLE_2_RUNECHAT prefs.save_preferences(src) to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_RUNECHAT) ? "now see" : "no longer see"] floating chat messages.") + +/client/verb/toggle_death_messages() + set name = "Show/Hide Death Notifications" + set category = "Preferences" + set desc = "Toggle player death notifications" + prefs.toggles2 ^= PREFTOGGLE_2_DEATHMESSAGE + prefs.save_preferences(src) + to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_DEATHMESSAGE) ? "now" : "no longer"] see a notification in deadchat when a player dies.") diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index 62a8b85194d..e2733ba2446 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -16,10 +16,10 @@ log transactions /obj/machinery/atm name = "Nanotrasen automatic teller machine" - desc = "For all your monetary needs!" + desc = "For all your monetary needs! Just insert your ID card to make a withdrawal or deposit!" icon = 'icons/obj/terminals.dmi' icon_state = "atm" - anchored = 1 + anchored = TRUE use_power = IDLE_POWER_USE idle_power_usage = 10 var/obj/machinery/computer/account_database/linked_db @@ -69,7 +69,7 @@ log transactions for(var/obj/item/stack/spacecash/S in T) cash_amount += S.amount if(cash_amount) - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, 1) + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, TRUE) for(var/obj/item/stack/spacecash/S in T) S.use(S.amount) authenticated_account.charge(-cash_amount, null, "Credit deposit", machine_id, "Terminal") @@ -110,7 +110,7 @@ log transactions /obj/machinery/atm/attack_hand(mob/user) if(..()) - return 1 + return TRUE if(issilicon(user)) to_chat(user, "Artificial unit recognized. Artificial units do not currently receive monetary compensation, as per Nanotrasen regulation #1005.") return @@ -155,6 +155,9 @@ log transactions return data /obj/machinery/atm/ui_act(action, params) + if(..()) + return + switch(action) if("transfer") if(!authenticated_account || !linked_db) @@ -237,7 +240,7 @@ log transactions previous_account_number = tried_account_num if("withdrawal") - var/amount = max(text2num(params["funds_amount"]),0) + var/amount = max(text2num(params["funds_amount"]), 0) if(amount <= 0) to_chat(usr, "[bicon(src)]That is not a valid amount.") else if(authenticated_account && amount > 0) @@ -301,4 +304,6 @@ log transactions //create the most effective combination of notes to make up the requested amount /obj/machinery/atm/proc/withdraw_arbitrary_sum(arbitrary_sum) - new /obj/item/stack/spacecash(get_step(get_turf(src), turn(dir, 180)), arbitrary_sum) + var/obj/item/stack/spacecash/C = new(amt = arbitrary_sum) + if(!usr?.put_in_hands(C)) + C.forceMove(get_step(get_turf(src), turn(dir, 180))) diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm index 5ff1c23685a..c6d0ba0fb37 100644 --- a/code/modules/mining/equipment/marker_beacons.dm +++ b/code/modules/mining/equipment/marker_beacons.dm @@ -107,15 +107,18 @@ GLOBAL_LIST_INIT(marker_beacon_colors, list( . = ..() if(.) return + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return to_chat(user, "You start picking [src] up...") if(do_after(user, remove_speed, target = src)) var/obj/item/stack/marker_beacon/M = new(loc) M.picked_color = picked_color M.update_icon() transfer_fingerprints_to(M) - if(user.put_in_hands(M, TRUE)) //delete the beacon if it fails - playsound(src, 'sound/items/deconstruct.ogg', 50, 1) - qdel(src) //otherwise delete us + user.put_in_hands(M) + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + qdel(src) /obj/structure/marker_beacon/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/stack/marker_beacon)) diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm index b0b16cdd6e8..f8730124425 100644 --- a/code/modules/mob/dead/observer/orbit.dm +++ b/code/modules/mob/dead/observer/orbit.dm @@ -58,12 +58,14 @@ var/mob/M = poi if(istype(M)) + if(isnewplayer(M)) // People in the lobby screen; only have their ckey as a name. + continue if(isobserver(M)) ghosts += list(serialized) - else if(M.stat == DEAD) - dead += list(serialized) else if(M.mind == null) npcs += list(serialized) + else if(M.stat == DEAD) + dead += list(serialized) else alive += list(serialized) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 4e3bbfcabfd..78489251ca1 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -254,26 +254,26 @@ if(wound_flavor_text["r_foot"]&& (is_destroyed["right foot"] || (!shoes && !skipshoes))) msg += wound_flavor_text["r_foot"] - var/temp = getBruteLoss() //no need to calculate each of these twice + var/damage = getBruteLoss() //no need to calculate each of these twice - if(temp) + if(damage) var/brute_message = !ismachineperson(src) ? "bruising" : "denting" - if(temp < 30) - msg += "[p_they(TRUE)] [p_have()] minor [brute_message ].\n" + if(damage < 60) + msg += "[p_they(TRUE)] [p_have()] [damage < 30 ? "minor" : "moderate"] [brute_message].\n" else - msg += "[p_they(TRUE)] [p_have()] severe [brute_message ]!\n" + msg += "[p_they(TRUE)] [p_have()] severe [brute_message]!\n" - temp = getFireLoss() - if(temp) - if(temp < 30) - msg += "[p_they(TRUE)] [p_have()] minor burns.\n" + damage = getFireLoss() + if(damage) + if(damage < 60) + msg += "[p_they(TRUE)] [p_have()] [damage < 30 ? "minor" : "moderate"] burns.\n" else msg += "[p_they(TRUE)] [p_have()] severe burns!\n" - temp = getCloneLoss() - if(temp) - if(temp < 30) - msg += "[p_they(TRUE)] [p_have()] minor cellular damage.\n" + damage = getCloneLoss() + if(damage) + if(damage < 60) + msg += "[p_they(TRUE)] [p_have()] [damage < 30 ? "minor" : "moderate"] cellular damage.\n" else msg += "[p_they(TRUE)] [p_have()] severe cellular damage.\n" diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 5bdc34cc728..0d831fe5760 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -94,6 +94,14 @@ mind.store_memory("Time of death: [station_time_timestamp("hh:mm:ss", timeofdeath)]", 0) GLOB.respawnable_list += src + if(mind.name && !isbrain(src)) // !isbrain() is to stop it from being called twice + var/turf/T = get_turf(src) + var/area_name = get_area_name(T) + for(var/P in GLOB.dead_mob_list) + var/mob/M = P + if((M.client?.prefs.toggles2 & PREFTOGGLE_2_DEATHMESSAGE) && (isobserver(M) || M.stat == DEAD)) + to_chat(M, "[mind.name] has died at [area_name]. (JMP)") + if(SSticker && SSticker.mode) SSticker.mode.check_win() if(mind && mind.devilinfo) // Expand this into a general-purpose death-response system when appropriate diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index a71c9733b70..a8ef52bdee8 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -107,7 +107,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/magpulse = 0 var/ionpulse = 0 // Jetpack-like effect. var/ionpulse_on = 0 // Jetpack-like effect. - var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. var/datum/action/item_action/toggle_research_scanner/scanner = null var/list/module_actions = list() @@ -176,6 +175,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( diag_hud_set_borgcell() scanner = new(src) scanner.Grant(src) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/create_trail) + +/mob/living/silicon/robot/proc/create_trail(datum/source, atom/oldloc, _dir, forced) + if(ionpulse_on) + var/turf/T = get_turf(oldloc) + if(!has_gravity(T)) + new /obj/effect/particle_effect/ion_trails(T, _dir) /mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) @@ -608,16 +614,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( to_chat(src, "No thrusters are installed!") return - if(!ion_trail) - ion_trail = new - ion_trail.set_up(src) - ionpulse_on = !ionpulse_on to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") - if(ionpulse_on) - ion_trail.start() - else - ion_trail.stop() if(thruster_button) thruster_button.icon_state = "ionpulse[ionpulse_on]" diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index b6189d56eac..7dfac96a807 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -29,16 +29,18 @@ faction = list("malf_drone") deathmessage = "suddenly breaks apart." del_on_death = 1 - var/datum/effect_system/trail_follow/ion/ion_trail var/passive_mode = TRUE // if true, don't target anything. /mob/living/simple_animal/hostile/malf_drone/Initialize(mapload) . = ..() - ion_trail = new - ion_trail.set_up(src) - ion_trail.start() + RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/create_trail) update_icons() +/mob/living/simple_animal/hostile/malf_drone/proc/create_trail(datum/source, atom/oldloc, _dir, forced) + var/turf/T = get_turf(oldloc) + if(!has_gravity(T)) + new /obj/effect/particle_effect/ion_trails(T, _dir) + /mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(check_drift = 0) return 1 diff --git a/code/modules/newscaster/obj/newspaper.dm b/code/modules/newscaster/obj/newspaper.dm index fe3ec257e32..025b404064f 100644 --- a/code/modules/newscaster/obj/newspaper.dm +++ b/code/modules/newscaster/obj/newspaper.dm @@ -12,6 +12,7 @@ desc = "An issue of The Griffon, the newspaper circulating aboard Nanotrasen Space Stations." icon = 'icons/obj/bureaucracy.dmi' icon_state = "newspaper" + item_state = "newspaper" w_class = WEIGHT_CLASS_SMALL attack_verb = list("bapped") /// The current screen to display. @@ -28,6 +29,8 @@ var/scribble = "" /// The page of said scribble. var/scribble_page = null + /// Whether the newspaper is rolled or not, making it a deadly weapon. + var/rolled = FALSE /obj/item/newspaper/Initialize(mapload) . = ..() @@ -35,6 +38,9 @@ news_content = list() /obj/item/newspaper/attack_self(mob/user) + if(rolled) + to_chat(user, "Unroll it first!") + return if(ishuman(user)) var/mob/living/carbon/human/human_user = user var/dat @@ -146,6 +152,9 @@ /obj/item/newspaper/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/pen)) + if(rolled) + to_chat(user, "Unroll it first!") + return if(scribble_page == curr_page) to_chat(user, "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?") else @@ -161,6 +170,17 @@ return return ..() +/obj/item/newspaper/AltClick(mob/user) + if(ishuman(user) && Adjacent(user) && !user.incapacitated()) + rolled = !rolled + icon_state = "newspaper[rolled ? "_rolled" : ""]" + update_icon() + var/verbtext = "[rolled ? "" : "un"]roll" + user.visible_message("[user] [verbtext]s [src].",\ + "You [verbtext] [src].") + name = "[rolled ? "rolled" : ""] [initial(name)]" + return ..() + #undef SCREEN_COVER #undef SCREEN_PAGE_INNER #undef SCREEN_PAGE_LAST diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index a014a6be34b..6d274a47ed3 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -187,6 +187,14 @@ build_path = /obj/item/pipe_painter category = list("initial", "Miscellaneous") +/datum/design/window_painter + name = "Window Painter" + id = "window_painter" + build_type = AUTOLATHE + materials = list(MAT_METAL = 5000, MAT_GLASS = 2000) + build_path = /obj/item/pipe_painter/window_painter + category = list("initial", "Miscellaneous") + /datum/design/floorpainter name = "Floor painter" id = "floor_painter" diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 2a50ccff03b..c6124104307 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -231,7 +231,6 @@ return mode = SHUTTLE_DOCKED timer = world.time - SSdiscord.send2discord_simple(DISCORD_WEBHOOK_PRIMARY, "The Emergency Shuttle has docked with the station.") emergency_shuttle_docked.Announce("The Emergency Shuttle has docked with the station. You have [timeLeft(600)] minutes to board the Emergency Shuttle.") /* diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 301e7b05d86..43a9a4a6ee7 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -38,8 +38,6 @@ var/obj/machinery/portable_atmospherics/canister/internal_tank var/use_internal_tank = 0 - var/datum/effect_system/trail_follow/ion/space_trail/ion_trail - var/hatch_open = 0 var/next_firetime = 0 @@ -111,9 +109,6 @@ battery = new battery_type(src) add_cabin() add_airtank() - src.ion_trail = new /datum/effect_system/trail_follow/ion/space_trail() - src.ion_trail.set_up(src) - src.ion_trail.start() src.use_internal_tank = 1 equipment_system = new(src) equipment_system.installed_modules += battery @@ -124,6 +119,29 @@ cargo_hold.max_w_class = 5 //fit almost anything cargo_hold.max_combined_w_class = 0 //you can optimize your stash with larger items START_PROCESSING(SSobj, src) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/create_trail) + +/obj/spacepod/proc/create_trail() + var/turf/T = get_turf(src) + var/atom/oldposition + var/atom/oldloc + switch(dir) + if(NORTH) + oldposition = get_step(T, SOUTH) + oldloc = get_step(oldposition, EAST) + if(SOUTH) // More difficult, offset to the north! + oldposition = get_step(get_step(src, NORTH), NORTH) + oldloc = get_step(oldposition, EAST) + if(EAST) // Just one to the north should suffice + oldposition = get_step(T, WEST) + oldloc = get_step(oldposition, NORTH) + if(WEST) // One to the east and north from there + oldposition = get_step(get_step(src, EAST), EAST) + oldloc = get_step(oldposition, NORTH) + + if(!has_gravity(T)) + new /obj/effect/particle_effect/ion_trails(oldposition, dir) + new /obj/effect/particle_effect/ion_trails(oldloc, dir) /obj/spacepod/Destroy() if(equipment_system.cargo_system) @@ -133,7 +151,6 @@ QDEL_NULL(battery) QDEL_NULL(cabin_air) QDEL_NULL(internal_tank) - QDEL_NULL(ion_trail) occupant_sanity_check() if(pilot) eject_pilot() @@ -280,7 +297,6 @@ H.forceMove(get_turf(src)) H.ex_act(severity + 1) to_chat(H, "You are forcefully thrown from [src]!") - qdel(ion_trail) qdel(src) if(2) deal_damage(100) diff --git a/code/modules/tgui/modules/power_monitor.dm b/code/modules/tgui/modules/power_monitor.dm index 4ef678768fa..4e1255607c7 100644 --- a/code/modules/tgui/modules/power_monitor.dm +++ b/code/modules/tgui/modules/power_monitor.dm @@ -29,14 +29,18 @@ data["select_monitor"] = TRUE data["powermonitors"] = GLOB.powermonitor_repository.powermonitor_data() - if(powermonitor && !isnull(powermonitor.powernet)) + if(powermonitor) if(select_monitor && (powermonitor.stat & (NOPOWER|BROKEN))) powermonitor = null return - data["poweravail"] = DisplayPower(powermonitor.powernet.viewavail) - data["powerdemand"] = DisplayPower(powermonitor.powernet.viewload) - data["history"] = powermonitor.history - data["apcs"] = GLOB.apc_repository.apc_data(powermonitor.powernet) + if(powermonitor.powernet) + data["poweravail"] = DisplayPower(powermonitor.powernet.viewavail) + data["powerdemand"] = DisplayPower(powermonitor.powernet.viewload) + data["history"] = powermonitor.history + data["apcs"] = GLOB.apc_repository.apc_data(powermonitor.powernet) + data["no_powernet"] = FALSE + else + data["no_powernet"] = TRUE return data diff --git a/config/example/config.txt b/config/example/config.txt index 54622806507..699e416053e 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -451,13 +451,13 @@ BYOND_ACCOUNT_AGE_THRESHOLD 7 ## IF YOU ARE DISABLING THIS COMMENT IT OUT ENTIRELY, DONT LEAVE IT BLANK #DISCORD_WEBHOOKS_ADMIN_ROLE_ID -## Webhook URL for the main discord webhook +## Webhook URLs for the main discord webhook. Separate multiple URLs with a | (EG: https://url1|https://url2) #DISCORD_WEBHOOKS_MAIN_URL -## Webhook URL for the admin discord webhook +## Webhook URLs for the admin discord webhook. Separate multiple URLs with a | (EG: https://url1|https://url2) #DISCORD_WEBHOOKS_ADMIN_URL -## Webhook URL for the mentor discord webhook +## Webhook URLs for the mentor discord webhook. Separate multiple URLs with a | (EG: https://url1|https://url2) #DISCORD_WEBHOOKS_MENTOR_URL ## Uncomment to send all ahelps to discord. If the line is commented, only ahelps made when no admins are online will be forwarded @@ -465,3 +465,9 @@ BYOND_ACCOUNT_AGE_THRESHOLD 7 #DISCORD_FORWARD_ALL_AHELPS ##### END DISCORD STUFF ##### + +## URL For CentCom global ban DB +## This is a config option should you want to disable this system, or if the primary URL changes +## THE TRAILING SLASH ON THE END IS IMPORTANT SINCE IT JUST APPENDS THE CKEY TO THE END IN RAW +## Add a hash before the line below to disable the system +CENTCOM_BAN_DB_URL https://centcom.melonmesa.com/ban/search/ diff --git a/goon/code/datums/browserOutput.dm b/goon/code/datums/browserOutput.dm index 11aca69220b..0550180ca67 100644 --- a/goon/code/datums/browserOutput.dm +++ b/goon/code/datums/browserOutput.dm @@ -190,7 +190,7 @@ var/list/chatResources = list( //Add autoban using the DB_ban_record function //Uh oh this fucker has a history of playing on a banned account!! if (found.len > 0) - message_admins("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])") + message_admins("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])") log_admin("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])") cookieSent = 1 diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi index 3f66fa90cd6..b9cc8b8ca2b 100644 Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 668d53ea644..a5fdfa24d1d 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/smooth_structures/fancy_table.dmi b/icons/obj/smooth_structures/fancy_table.dmi index c675550ed5a..c844ec5cedd 100644 Binary files a/icons/obj/smooth_structures/fancy_table.dmi and b/icons/obj/smooth_structures/fancy_table.dmi differ diff --git a/icons/obj/smooth_structures/fancy_table_black.dmi b/icons/obj/smooth_structures/fancy_table_black.dmi index 8275c48d3c6..7932c0465b9 100644 Binary files a/icons/obj/smooth_structures/fancy_table_black.dmi and b/icons/obj/smooth_structures/fancy_table_black.dmi differ diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi index 87e919f1f09..ae3d0ff10ee 100755 Binary files a/icons/turf/areas.dmi and b/icons/turf/areas.dmi differ diff --git a/paradise.dme b/paradise.dme index e4193e3b19e..998a1c21a20 100644 --- a/paradise.dme +++ b/paradise.dme @@ -296,6 +296,7 @@ #include "code\datums\progressbar.dm" #include "code\datums\radio.dm" #include "code\datums\recipe.dm" +#include "code\datums\revision.dm" #include "code\datums\ruins.dm" #include "code\datums\shuttles.dm" #include "code\datums\soullink.dm" @@ -913,6 +914,7 @@ #include "code\game\objects\items\devices\uplinks.dm" #include "code\game\objects\items\devices\voice.dm" #include "code\game\objects\items\devices\whistle.dm" +#include "code\game\objects\items\devices\window_painter.dm" #include "code\game\objects\items\devices\radio\beacon.dm" #include "code\game\objects\items\devices\radio\electropack.dm" #include "code\game\objects\items\devices\radio\encryptionkey.dm" @@ -1196,6 +1198,7 @@ #include "code\modules\admin\admin_verbs.dm" #include "code\modules\admin\banappearance.dm" #include "code\modules\admin\banjob.dm" +#include "code\modules\admin\centcom_ban_db.dm" #include "code\modules\admin\create_mob.dm" #include "code\modules\admin\create_object.dm" #include "code\modules\admin\create_poll.dm" diff --git a/tgui/packages/tgui/interfaces/PowerMonitor.js b/tgui/packages/tgui/interfaces/PowerMonitor.js index 660c57021db..a4cd5c81541 100644 --- a/tgui/packages/tgui/interfaces/PowerMonitor.js +++ b/tgui/packages/tgui/interfaces/PowerMonitor.js @@ -2,11 +2,11 @@ import { map, sortBy } from 'common/collections'; import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { pureComponentHooks } from 'common/react'; -import { Component, Fragment } from 'inferno'; import { decodeHtmlEntities } from 'common/string'; -import { Section, Box, Button, Flex, LabeledList, ProgressBar, ColorBox, Chart, Table, Icon } from "../components"; -import { Window } from "../layouts"; +import { Fragment } from 'inferno'; import { useBackend, useLocalState } from "../backend"; +import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from "../components"; +import { Window } from "../layouts"; const PEAK_DRAW = 600000; export const PowerMonitor = (props, context) => { @@ -70,31 +70,162 @@ const DataView = (props, context) => { history, apcs, select_monitor, + no_powernet, } = data; - const [ - sortByField, - setSortByField, - ] = useLocalState(context, 'sortByField', null); - const supply = history.supply[history.supply.length - 1] || 0; - const demand = history.demand[history.demand.length - 1] || 0; - const supplyData = history.supply.map((value, i) => [i, value]); - const demandData = history.demand.map((value, i) => [i, value]); - const maxValue = Math.max( - PEAK_DRAW, - ...history.supply, - ...history.demand); - // Process area data - const parsedApcs = flow([ - map((apc, i) => ({ - ...apc, - // Generate a unique id - id: apc.name + i, - })), - sortByField === 'name' && sortBy(apc => apc.Name), - sortByField === 'charge' && sortBy(apc => -apc.CellPct), - sortByField === 'draw' && sortBy(apc => -apc.Load), - ])(apcs); + let body; + if (no_powernet) { + body = ( + +
+ Warning: The monitor is not connected to power grid via cable! +
+ ); + } else { + const [ + sortByField, + setSortByField, + ] = useLocalState(context, 'sortByField', null); + const supply = history.supply[history.supply.length - 1] || 0; + const demand = history.demand[history.demand.length - 1] || 0; + const supplyData = history.supply.map((value, i) => [i, value]); + const demandData = history.demand.map((value, i) => [i, value]); + const maxValue = Math.max( + PEAK_DRAW, + ...history.supply, + ...history.demand); + // Process area data + const parsedApcs = flow([ + map((apc, i) => ({ + ...apc, + // Generate a unique id + id: apc.name + i, + })), + sortByField === 'name' && sortBy(apc => apc.Name), + sortByField === 'charge' && sortBy(apc => -apc.CellPct), + sortByField === 'draw' && sortBy(apc => -apc.Load), + ])(apcs); + + body = ( + + + +
+ + + + {toFixed(supply / 1000) + ' kW'} + + + + + {toFixed(demand / 1000) + ' kW'} + + + +
+
+ +
+ + +
+
+
+ + + Sort by: + + setSortByField(sortByField !== 'name' && 'name')} /> + setSortByField( + sortByField !== 'charge' && 'charge' + )} /> + setSortByField(sortByField !== 'draw' && 'draw')} /> + + + + + Area + + + Charge + + + Draw + + + Eqp + + + Lgt + + + Env + + + {parsedApcs.map((area, i) => ( + + + {decodeHtmlEntities(area.Name)} + + + + + + {area.Load} + + + + + + + + + + + + ))} +
+
+ ); + } return (
{ )} }> - - -
- - - - {toFixed(supply / 1000) + ' kW'} - - - - - {toFixed(demand / 1000) + ' kW'} - - - -
-
- -
- - -
-
-
- - - Sort by: - - setSortByField(sortByField !== 'name' && 'name')} /> - setSortByField( - sortByField !== 'charge' && 'charge' - )} /> - setSortByField(sortByField !== 'draw' && 'draw')} /> - - - - - Area - - - Charge - - - Draw - - - Eqp - - - Lgt - - - Env - - - {parsedApcs.map((area, i) => ( - - - {decodeHtmlEntities(area.Name)} - - - - - - {area.Load} - - - - - - - - - - - - ))} -
+ {body}
); }; diff --git a/tgui/packages/tgui/interfaces/SecurityRecords.js b/tgui/packages/tgui/interfaces/SecurityRecords.js index b1edc242e11..2ded47aaaff 100644 --- a/tgui/packages/tgui/interfaces/SecurityRecords.js +++ b/tgui/packages/tgui/interfaces/SecurityRecords.js @@ -15,6 +15,9 @@ const statusStyles = { "Incarcerated": "incarcerated", "Parolled": "parolled", "Released": "released", + "Demote": "demote", + "Search": "search", + "Monitor": "monitor", }; const doEdit = (context, field) => { diff --git a/tgui/packages/tgui/interfaces/Sleeper.js b/tgui/packages/tgui/interfaces/Sleeper.js index 7a464923cf8..a38886742a7 100644 --- a/tgui/packages/tgui/interfaces/Sleeper.js +++ b/tgui/packages/tgui/interfaces/Sleeper.js @@ -42,6 +42,7 @@ export const Sleeper = (props, context) => { {body} + ); @@ -57,7 +58,6 @@ const SleeperMain = (props, context) => { - ); }; @@ -174,6 +174,7 @@ const SleeperDamage = (props, context) => { const SleeperDialysis = (props, context) => { const { act, data } = useBackend(context); const { + hasOccupant, isBeakerLoaded, beakerMaxSpace, beakerFreeSpace, @@ -186,7 +187,7 @@ const SleeperDialysis = (props, context) => { buttons={