diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index c98d8a93f8b8..000000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,904 +0,0 @@ -# CONTRIBUTING - -## Introduction - -This is the contribution guide for Paradise Station. These guidelines apply to -both new issues and new pull requests. If you are making a pull request, please refer to -the [Pull request](#pull-requests) section, and if you are making an issue report, please -refer to the [Issue Report](#issues) section, as well as the -[Issue Report Template](ISSUE_TEMPLATE.md). - -## Commenting - -If you comment on an active pull request or issue report, make sure your comment is -concise and to the point. Comments on issue reports or pull requests should be relevant -and friendly, not attacks on the author or adages about something minimally relevant. -If you believe an issue report is not a "bug", please point out specifically and concisely your reasoning in a comment on the issue itself. - -### Comment Guidelines - -- Comments on Pull Requests and Issues should remain relevant to the subject in question and not derail discussions. -- Under no circumstances are users to be attacked for their ideas or contributions. All participants on a given PR or issue are expected to be civil. Failure to do so will result in disciplinary action. -- For more details, see the [Code of Conduct](../CODE_OF_CONDUCT.md). - -## Issues - -The Issues section is not a place to request features, or ask for things to be changed -because you think they should be that way; The Issues section is specifically for -reporting bugs in the code. - -### Issue Guidelines - -- Issue reports should be as detailed as possible, and if applicable, should include instructions on how to reproduce the bug. - -## Pull requests - -Players are welcome to participate in the development of this fork and submit their own -pull requests. If the work you are submitting is a new feature, or affects balance, it is -strongly recommended you get approval/traction for it from our forums before starting the -actual development. - -### Pull Request Guidelines - -- Keep your pull requests atomic. Each pull request should strive to address one primary goal, and should not include fixes or changes that aren't related to the main purpose of the pull request. Unrelated changes should be applied in new pull requests. In case of mapping PRs that add features - consult a member of the development team on whether it would be appropriate to split up the PR to add the feature to multiple maps individually. - -- Document and explain your pull requests thoroughly. Failure to do so will delay a PR as we question why changes were made. This is especially important if you're porting a PR from another codebase (i.e. TG) and divert from the original. Explaining with single comment on why you've made changes will help us review the PR faster and understand your decision making process. - -- Any pull request must have a changelog, this is to allow us to know when a PR is deployed on the live server. Inline changelogs are supported through the format described [here](https://github.com/ParadiseSS13/Paradise/pull/3291#issuecomment-172950466) and should be used rather than manually edited .yml file changelogs. - -- Pull requests should not have any merge commits except in the case of fixing merge conflicts for an existing pull request. New pull requests should not have any merge commits. Use `git rebase` or `git reset` to update your branches, not `git pull`. - -- Please explain why you are submitting the pull request, and how you think your change will be beneficial to the game. Failure to do so will be grounds for rejecting the PR. - -- If your pull request is not finished make sure it is at least testable in a live environment. Pull requests that do not at least meet this requirement may be closed at maintainer discretion. You may request a maintainer reopen the pull request when you're ready, or make a new one. - -- While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality _before_ you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes. - -- By ticking or leaving ticked the option "Allow edits and access to secrets by maintainers", either when making a PR or at any time thereafter, you give permission for repository maintainers to push changes to your branch without explicit permission. Repository maintainers will avoid doing this unless necessary, and generally should only use it to apply a merge upstream/master, rebuild TGUI, deconflict maps, or other minor changes required shortly before a PR is to be merged. More extensive changes such as force-pushes to your branch require explicit permission from the PR author each time such a change needs to be made. - -#### Using The Changelog - -- The tags able to be used in the changelog are: `add/soundadd/imageadd`, `del/sounddel/imagedel`, `tweak`, `fix`, `wip`, `spellcheck`, and `experiment`. -- Without specifying a name it will default to using your GitHub name. Some examples include: - -```txt - :cl: - add: The ability to change the color of wires - del: Deleted depreciated wire merging now handled in parent - fix: Moving wires now follows the user input instead of moving the stack - /:cl: -``` - -```txt - :cl: UsernameHere - spellcheck: Fixes some misspelled words under Using Changelog - /:cl: -``` - -## Modifying MILLA - -Our atmos engine, MILLA, is in the `milla/` directory. It's written in Rust for performance reasons, which means it's not compiled the same way as the rest of the code. If you're on Windows, you get a pre-built copy by default. If you're on Linux, you built one already to run the server. - -If you make changes to MILLA, you'll want to rebuild. This will be very similar to RUSTG: -https://github.com/ParadiseSS13/rust-g -The only difference is that you run `cargo` from the `milla/` directory, and don't need to speify `--all-features` (though it doesn't hurt). - -The server will automatically detect that you have a local build, and use that over the default Windows one. - -When you're ready to make a PR, please DO NOT modify `milla.dll` or `tools/ci/libmilla_ci.so`. Leave "Allow edits and access to secrets by maintainers" enabled, and post a comment on your PR saying `!build_milla`. A bot will automatically build them for you and update your branch. - -## Specifications - -As mentioned before, you are expected to follow these specifications in order to make everyone's lives easier. It'll save both your time and ours, by making -sure you don't have to make any changes and we don't have to ask you to. Thank you for reading this section! - -### Object Oriented Code - -As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding -content to it. If you don't know what "object-oriented" means, we highly recommend you do some light research to grasp the basics. - -### All BYOND paths must contain the full path - -(i.e. absolute pathing) - -DM will allow you nest almost any type keyword into a block, such as: - -```dm -datum - datum1 - var - varname1 = 1 - varname2 - static - varname3 - varname4 - proc - proc1() - code - proc2() - code - - datum2 - varname1 = 0 - proc - proc3() - code - proc2() - ..() - code -``` - -The use of this format is **not** allowed in this project, as it makes finding definitions via full text searching next to impossible. The only exception is the variables of an object may be nested to the object, but must not nest further. - -The previous code made compliant: - -```dm -/datum/datum1 - var/varname1 = 1 - var/varname2 - var/static/varname3 - var/static/varname4 - -/datum/datum1/proc/proc1() - code - -/datum/datum1/proc/proc2() - code - -/datum/datum1/datum2 - varname1 = 0 - -/datum/datum1/datum2/proc/proc3() - code - -/datum/datum1/datum2/proc2() - ..() - code -``` - -### Do not compare boolean values to TRUE or FALSE - -Do not compare boolean values to TRUE or FALSE. For TRUE you should just check if there's a value in that address. For FALSE you should use the ! operator. An exception is made to this when working with JS or other external languages. If a function/variable can contain more values beyond null/0 or TRUE, use numbers and defines instead of true/false comparisons. - -```dm -// Bad -var/thing = pick(TRUE, FALSE) -if(thing == TRUE) - return "bleh" -var/other_thing = pick(TRUE, FALSE) -if(other_thing == FALSE) - return "meh" - -// Good -var/thing = pick(TRUE, FALSE) -if(thing) - return "bleh" -var/other_thing = pick(TRUE, FALSE) -if(!other_thing) - return "meh" -``` - -### Use `pick(x, y, z)`, not `pick(list(x, y, z))` - -`pick()` will happily take a fixed set of options. Wrapping them in a list is redundant and slightly less efficient. -'''dm -// Bad -var/text = pick(list("test_1", "test_2", "test_3")) -to_chat(world, text) - -// Good -var/text = pick("test_1", "test_2", "test_3") -to_chat(world, text) -''' - -### User Interfaces - -All new user interfaces in the game must be created using the TGUI framework. Documentation can be found inside the [`tgui/docs`](../tgui/docs) folder, and the [`README.md`](../tgui/README.md) file. This is to ensure all ingame UIs are snappy and respond well. An exception is made for user interfaces which are purely for OOC actions (Such as character creation, or anything admin related) - -### No overriding type safety checks - -The use of the `:` operator to override type safety checks is not allowed. You must cast the variable to the proper type. - -### Do not access return value vars directly from functions - -The use of the pointer operator, `.`, should not be used to access the return values of functions directly. This can cause unintended behavior and is difficult to read. - -```dm -//Bad -var/our_x = get_turf(thing).x - -//Good -var/turf/our_turf = get_turf(thing) -var/our_x = our_turf.x -``` - -### Type paths must begin with a / - -eg: `/datum/thing`, not `datum/thing` - -### Datum type paths must began with "datum" - -In DM, this is optional, but omitting it makes finding definitions harder. To be specific, you can declare the path `/arbitrary`, but it -will still be, in actuality, `/datum/arbitrary`. Write your code to reflect this. - -### Do not use list operators in strings - -The use of list operators to augment strings is not allowed. This is roughly 10 times slower than using a list with a Join() Function. - -```dm -//Bad -var/text = "text" -text += "More text" -to_chat(world, text) - -//Good -var/list/text = list("text") -text += "More text" -to_chat(world, text.Join("")) -``` - -### Do not use text/string based type paths - -It is rarely allowed to put type paths in a text format, as there are no compile errors if the type path no longer exists. Here is an example: - -```dm -//Bad -var/path_type = "/obj/item/baseball_bat" - -//Good -var/path_type = /obj/item/baseball_bat -``` - -### Do not use `\The` - -The `\The` macro doesn't actually do anything when used in the format `\The [atom reference]`. Directly referencing an atom in an embedded string -will automatically prefix `The` or `the` to it as appropriate. As an extension, when referencing an atom, don't use `[atom.name]`, use `[atom]`. -The only exception to this rule is when dealing with items "belonging" to a mob, in which case you should use `[mob]'s [atom.name]` to avoid `The` -ever forming. - -```dm -//Bad -var/atom/A -"\The [A]" - -//Good -var/atom/A -"[A]" -``` - -### Use the pronoun library instead of `\his` macros - -We have a system in [`code/__HELPERS/pronouns.dm`](../code/__HELPERS/pronouns.dm) for addressing all forms of pronouns. This is useful in a number of ways; - -- BYOND's `\his` macro can be unpredictable on what object it references. Take this example: `"[user] waves \his [user.weapon] around, hitting \his opponents!"`. This will end up referencing the user's gender in the first occurence, but what about the second? It'll actually print the gender set on the weapon he's carrying, which is unintended - and there's no way around this. -- It always prints the real `gender` variable of the atom it's referencing. This can lead to exposing a mob's gender even when their face is covered, which would normally prevent it's gender from being printed. - -The way to avoid these problems is to use the pronoun system. Instead of `"[user] waves \his arms."`, you can do `"[user] waves [user.p_their()] arms."` - -```dm -//Bad -"[H] waves \his hands!" -"[user] waves \his [user.weapon] around, hitting \his opponents!" - -//Good -"[H] waves [H.p_their()] hands!" -"[user] waves [H.p_their()] [user.weapon] around, hitting [H.p_their()] opponents!"` -``` - -### Use `[A.UID()]` over `\ref[A]` - -BYOND has a system to pass "soft references" to datums, using the format `"\ref[datum]"` inside a string. This allows you to find the object just based -off of a text string, which is especially useful when dealing with the bridge between BYOND code and HTML/JS in UIs. It's resolved back into an object -reference by using `locate("\ref[datum]")` when the code comes back to BYOND. The issue with this is that locate() can return a unexpected datum -if the original datum has been deleted - BYOND recycles the references. - -UID's are actually unique; they work off of a global counter and are not recycled. Each datum has one assigned to it when it's created, which can be -accessed by `[datum.UID()]`. You can use this as a snap-in replacement for `\ref` by changing any `locate(ref)` calls in your code to `locateUID(ref)`. -Usage of this system is mandatory for any `Topic()` calls, and will produce errors in Dream Daemon if it's not used. - -```dm -//Bad -"Link!" - -//Good -"Link!" -``` - -### Use `var/name` format when declaring variables - -While DM allows other ways of declaring variables, this one should be used for consistency. - -### Tabs, not spaces - -You must use tabs to indent your code, NOT SPACES. - -(You may use spaces to align something, but you should tab to the block level first, then add the remaining spaces.) - -### No hacky code - -Hacky code, such as adding specific checks (ex: `istype(src, /obj/whatever)`), is highly discouraged and only allowed when there is **_no_** other option. (Protip: 'I couldn't immediately think of a proper way so thus there must be no other option' is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers, PR Reviewers, and other contributors who can help you exist for exactly that reason.) - -You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and -then overriding them as required. - -The same also applies to bugfixes - If an invalid value is being passed into a proc from something that shouldn't have that value, don't fix it on the proc itself, fix it at its origin! (Where feasible) - -### No duplicated code - -Copying code from one place to another may be suitable for small, short-time projects, but Paradise is a long-term project and highly discourages this. - -Instead you can use object orientation, or simply placing repeated code in a function, to obey this specification easily. - -### Startup/Runtime tradeoffs with lists and the "hidden" init proc - -First, read the comments in [this BYOND thread](http://www.byond.com/forum/?post=2086980&page=2#comment19776775), starting where the link takes you. - -There are two key points here: - -1. Defining a list in the variable's definition calls a hidden proc - init. If you have to define a list at startup, do so in `New()` (or preferably `Initialize()`) and avoid the overhead of a second call (`init()` and then `New()`) - -2. It also consumes more memory to the point where the list is actually required, even if the object in question may never use it! - -Remember: although this tradeoff makes sense in many cases, it doesn't cover them all. Think carefully about your addition before deciding if you need to use it. - -### Prefer `Initialize()` over `New()` for atoms - -Our game controller is pretty good at handling long operations and lag, but it can't control what happens when the map is loaded, which calls `New()` for all atoms on the map. If you're creating a new atom, use the `Initialize()` proc to do what you would normally do in `New()`. This cuts down on the number of proc calls needed when the world is loaded. - -While we normally encourage (and in some cases, even require) bringing out of date code up to date when you make unrelated changes near the out of date code, that is not the case for `New()` -> `Initialize()` conversions. These systems are generally more dependent on parent and children procs, so unrelated random conversions of existing things can cause bugs that take months to figure out. - -### No implicit `var/` - -When you declare a parameter in a proc, the `var/` is implicit. Do not include any implicit `var/` when declaring a variable. - -```dm -//Bad -/obj/item/proc1(var/mob/input1, var/input2) - code - -//Good -/obj/item/proc1(mob/input1, input2) - code -``` - -### No magic numbers or strings - -This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that more clearly states what it's for. For instance: - -```dm -//Bad -/datum/proc/do_the_thing(thing_to_do) - switch(thing_to_do) - if(1) - do_stuff() - if(2) - do_other_stuff() -``` - -There's no indication of what "1" and "2" mean! Instead, you should do something like this: - -```dm -//Good -#define DO_THE_THING_REALLY_HARD 1 -#define DO_THE_THING_EFFICIENTLY 2 - -/datum/proc/do_the_thing(thing_to_do) - switch(thing_to_do) - if(DO_THE_THING_REALLY_HARD) - do_stuff() - if(DO_THE_THING_EFFICIENTLY) - do_other_stuff() -``` - -This is clearer and enhances readability of your code! Get used to doing it! - -### Control statements - -(if, while, for, etc) - -- All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse - (eg: `if(count <= 10)` not `if(10 >= count)`) -- All control statements must be spaced as `if()`, with the brackets touching the keyword. -- All control statements must not contain code on the same line as the statement. - - ```DM - //Bad - if(x) return - - //Good - if(x) - return - ``` - -### Player Output - -Due to the use of "TGchat", Paradise requires a special syntax for outputting text messages to players. Instead of `mob << "message"`, you must use `to_chat(mob, "message")`. Failure to do so will lead to your code not working. - -### Use early returns - -Do not enclose a proc in an if-block when returning on a condition is more feasible. - -This is bad: - -```dm -/datum/datum1/proc/proc1() - if(thing1) - if(!thing2) - if(thing3 == 30) - do stuff -``` - -This is good: - -```dm -/datum/datum1/proc/proc1() - if(!thing1) - return - if(thing2) - return - if(thing3 != 30) - return - do stuff -``` - -This prevents nesting levels from getting deeper then they need to be. - -### Use `addtimer()` instead of `sleep()` or `spawn()` - -If you need to call a proc after a set amount of time, use `addtimer()` instead of `spawn()` / `sleep()` where feasible. -Though more complex, this method has greater performance. Additionally, unlike `spawn()` or `sleep()`, it can be cancelled. -For more details, see [https://github.com/tgstation/tgstation/pull/22933](https://github.com/tgstation/tgstation/pull/22933). - -Look for code examples on how to properly use it. - -```dm -//Bad -/datum/datum1/proc/proc1(target) - spawn(5 SECONDS) - target.dothing(arg1, arg2, arg3) - -//Good -/datum/datum1/proc/proc1(target) - addtimer(CALLBACK(target, PROC_REF(dothing), arg1, arg2, arg3), 5 SECONDS) -``` - -### Signals - -Signals are a slightly more advanced topic, but are often useful for attaching external behavior to objects that should be triggered when a specific event occurs. - -When defining procs that should be called by signals, you must include `SIGNAL_HANDLER` after the proc header. This ensures that no sleeping code can be called from within a signal handler, as that can cause problems with the signal system. - -Since callbacks can be connected to many signals with `RegisterSignal`, it can be difficult to pin down the source that a callback is invoked from. Any new `SIGNAL_HANDLER` should be followed by a comment listing the signals that the proc is expected to be invoked for. If there are multiple signals to be handled, separate them with a `+`. - -```dm -/atom/movable/proc/when_moved(atom/movable/A) - SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED - do_something() - -/datum/component/foo/proc/on_enter(datum/source, atom/enterer) - SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + COMSIG_ATOM_INITIALIZED_ON - do_something_else() -``` - -If your proc does have something that needs to sleep (such as a `do_after()`), do not simply omit the `SIGNAL_HANDLER`. Instead, call the sleeping code with `INVOKE_ASYNC` from within the signal handling function. - -```dm -/atom/movable/proc/when_moved(atom/movable/A) - SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED - INVOKE_ASYNC(src, PROC_REF(thing_that_sleeps), arg1) -``` - -### Operators - -#### Spacing of operators - -- Operators that should be separated by spaces: - - Boolean and logic operators like `&&`, `||` `<`, `>`, `==`, etc. (But not `!`) - - Bitwise AND `&` and OR `|`. - - Argument separator operators like `,`. (and `;` when used in a forloop) - - Assignment operators like `=` or `+=` or the like. - - Math operators like `+`, `-`, `/`, or `*`. -- Operators that should NOT be separated by spaces: - - Access operators like `.` and `:`. - - Parentheses `()`. - - Logical not `!`. - -#### Use of operators - -- Bitwise AND `&` - - Should be written as `bitfield & bitflag` NEVER `bitflag & bitfield`, both are valid, but the latter is confusing and nonstandard. -- Associated lists declarations must have their key value quoted if it's a string - -```DM - //Bad - list(a = "b") - - //Good - list("a" = "b") -``` - -#### Bitflags - -- Bitshift operators are mandatory, opposed to directly typing out the value. I.E: - -```dm - #define MACRO_ONE (1<<0) - #define MACRO_TWO (1<<1) - #define MACRO_THREE (1<<2) -``` - -Is accepted, whereas the following is not: - -```dm - #define MACRO_ONE 1 - #define MACRO_TWO 2 - #define MACRO_THREE 4 -``` - -While it may initially look intimidating, `(1<Arbitrary text") - - //Good - user.visible_message("Arbitrary text") -``` - -- You should not use color macros (`\red, \blue, \green, \black`) to color text, instead, you should use span classes. `Red text`, `Blue text`. - -```dm - //Bad - to_chat(user, "\red Red text \black Black text") - - //Good - to_chat(user, "Red textBlack text") -``` - -- To use variables in strings, you should **never** use the `text()` operator, use embedded expressions directly in the string. - -```dm - //Bad - to_chat(user, text("[] is leaking []!", name, liquid_type)) - - //Good - to_chat(user, "[name] is leaking [liquid_type]!") -``` - -- To reference a variable/proc on the src object, you should **not** use `src.var`/`src.proc()`. The `src.` in these cases is implied, so you should just use `var`/`proc()`. - -```dm - //Bad - var/user = src.interactor - src.fill_reserves(user) - - //Good - var/user = interactor - fill_reserves(user) -``` - -### Develop Secure Code - -- Player input must always be escaped safely, we recommend you use `stripped_input()` in all cases where you would use input. Essentially, just always treat input from players as inherently malicious and design with that use case in mind. - -- Calls to the database must be escaped properly - use proper parameters (values starting with a :). You can then replace these with a list of parameters, and these will be properly escaped during the query, and prevent any SQL injection. - -```dm - //Bad - var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey='[target_ckey]'") - - //Good - var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey=:target_ckey", list( - "target_ckey" = target_ckey - )) // Note the use of parameters on the above line and :target_ckey in the query. -``` - -- All calls to topics must be checked for correctness. Topic href calls can be easily faked by clients, so you should ensure that the call is valid for the state the item is in. Do not rely on the UI code to provide only valid topic calls, because it won't. - -- Information that players could use to metagame (that is, to identify round information and/or antagonist type via information that would not be available to them in character) should be kept as administrator only. - -- Where you have code that can cause large-scale modification and _FUN_, make sure you start it out locked behind one of the default admin roles - use common sense to determine which role fits the level of damage a function could do. - -### Files - -- Because runtime errors do not give the full path, try to avoid having files with the same name across folders. - -- File names should not be mixed case, or contain spaces or any character that would require escaping in a uri. - -- Files and path accessed and referenced by code above simply being #included should be strictly lowercase to avoid issues on filesystems where case matters. - -#### Modular Code in a File - -Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones (i.e. a new tool would go in the `tools.dm` file) - -Our codebase also has support for checking files so that they only contain one specific typepath, including none of its subtypes. This can be done by adding a specific header at the beginning of the file, which the CI will look for when running. An example can be seen below. You can also run this test locally using `/tools/ci/restrict_file_types.py` - -```dm -RESTRICT_TYPE(/datum/foo) - -/datum/proc/do_thing() // Error: '/datum' proc found in a file restricted to '/datum/foo' - -/datum/foo - -/datum/foo/do_thing() - -/datum/foo/bar // Error: '/datum/foo/bar' type definition found in a file restricted to '/datum/foo' - -/datum/foo/bar/do_thing() // Error: '/datum/foo/bar' proc found in a file restricted to '/datum/foo' -``` - -### SQL - -- Do not use the shorthand sql insert format (where no column names are specified) because it unnecessarily breaks all queries on minor column changes and prevents using these tables for tracking outside related info such as in a connected site/forum. - -- Use parameters for queries, as mentioned above in [Develop Secure Code](#develop-secure-code). - -- Always check your queries for success with `if(!query.warn_execute())`. By using this standard format, you can ensure the correct log messages are used. - -- Always `qdel()` your queries after you are done with them, this cleans up the results and helps things run smoother. - -- All changes to the database's layout (schema) must be specified in the database changelog in SQL, as well as reflected in the schema file. - -- Any time the schema is changed the `SQL_VERSION` defines must be incremented, as well as the example config, with an appropriate conversion kit placed - in the SQL/updates folder. - -- Queries must never specify the database, be it in code, or in text files in the repo. - -### Mapping Standards - -- For map edit PRs, we do not accept 'change for the sake of change' remaps, unless you have very good reasoning to do so. Maintainers reserve the right to close your PR if we disagree with your reasoning. - -- Map Merge - - - The following guideline for map merging applies to **ALL** mapping contributers. - - Before committing a map change, you **MUST** run mapmerge2 to normalise your changes. You can do this manually before every commit with `"\tools\mapmerge2\Run Before Committing.bat"` or automatically by installing the hooks at `"\tools\hooks\Install.bat"`. - - Failure to run Map Merge on a map after editing greatly increases the risk of the map's key dictionary becoming corrupted by future edits after running map merge. Resolving the corruption issue involves rebuilding the map's key dictionary; - -- StrongDMM - - - [We strongly encourage use of StrongDMM version 2 or greater, available here.](https://github.com/SpaiR/StrongDMM/releases) - - When using StrongDMM, the following options must be enabled. They can be found under `File > Preferences`. - - Sanitize Variables - Removes variables that are declared on the map, but are the same as initial. (For example: A standard floor turf that has `dir = 2` declared on the map will have that variable deleted as it is redundant.) - - Save format - `TGM`. - - Nudge mode - pixel_x/pixel_y - -- Variable Editing (Var-edits) - - - While var-editing an item within the editor is fine, it is preferred that when you are changing the base behavior of an item (how it functions) that you make a new subtype of that item within the code, especially if you plan to use the item in multiple locations on the same map, or across multiple maps. This makes it easier to make corrections as needed to all instances of the item at one time, as opposed to having to find each instance of it and change them all individually. - - Subtypes only intended to be used on ruin maps should be contained within an .dm file with a name corresponding to that map within `code\modules\ruins`. This is so in the event that the map is removed, that subtype will be removed at the same time as well to minimize leftover/unused data within the repo. - - When not using StrongDMM (which handles the following automatically) please attempt to clean out any dirty variables that may be contained within items you alter through var-editing. For example changing the `pixel_x` variable from 23 to 0 will leave a dirty record in the map's code of `pixel_x = 0`. - - Areas should **never** be var-edited on a map. All areas of a single type, altered instance or not, are considered the same area within the code, and editing their variables on a map can lead to issues with powernets and event subsystems which are difficult to debug. - - Unless they require custom placement, when placing the following items use the relevant "[direction] bump" instance, as it has predefined pixel offsets and directions that are standardised: APC, Air alarm, Fire alarm, station intercom, newscaster, extinguisher cabient, light switches. - -- If you are making non-minor edits to an area or room, (non-minor being anything more than moving a few objects or fixing small bugs) then you should ensure the entire area/room is updated to meet these standards. - -- When making a change to an area or room, follow these guidelines: - - - Unless absolutely necessary, do not run pipes (including disposals) under wall turfs. - - **NEVER** run cables under wall turfs. - - Keep floor turf variations to a minimum. Generally, more than 3 floor turf types in one room is bad design. - - Run air pipes together where possible. The first example below is to be avoided, the second is optimal: - - ![image](https://user-images.githubusercontent.com/12197162/120011088-d22c7400-bfd5-11eb-867f-7b137ac5b1b2.png) ![image](https://user-images.githubusercontent.com/12197162/120011126-dfe1f980-bfd5-11eb-96b2-c83238a9cdcf.png) - - - Pipe layouts should be logical and predictable, easy to understand at a glance. Always avoid complex layouts like in this example: - - ![image](https://user-images.githubusercontent.com/12197162/120619480-ecda6f00-c453-11eb-9d9f-abf0d1a99c34.png) - - - Decals are to be used sparingly. Good map design does not require warning tape around everything. Decal overuse contributes to maptick slowdown. - - Every **area** should contain only one APC and air alarm. - - Critical infrastructure rooms (such as the engine, arrivals, and medbay areas) should be given an APC with a larger power cell. - - Every **room** should contain at least one fire alarm, air vent and scrubber, light switch, station intercom, and security camera. - - Intercoms should be set to frequency 145.9, and be speaker ON Microphone OFF. This is so radio signals can reach people even without headsets on. Larger room will require more than one at a time. - - Exceptions can be made to security camera placement for certain rooms, such as the execution room. Larger rooms may require more than one security camera. All security cameras should have a descriptive name that makes it easy to find on a camera console. - - A good example would be the template [Department name] - [Area], so Brig - Cell 1, or Medbay - Treatment Center. Consistency is key to good camera naming. - - Fire alarms should not be placed next to expected heat sources. - - Use the following "on" subtype of vents and scrubbers as opposed to var-editing: `/obj/machinery/atmospherics/unary/vent_scrubber/on` and `/obj/machinery/atmospherics/unary/vent_pump/on` - - Head of staff offices should contain a requests console. - - Electrochromic windows (`/obj/structure/window/reinforced/polarized`) and doors/windoors (using the `/obj/effect/mapping_helpers/airlock/polarized` helper) are preferred over shutters as the method of restricting view to a room through windows. Shutters are sill appropriate in industrial/hazardous areas of the station (engine rooms, HoP line, science test chamber, etc.). - - Electrochromic window/windoor/door sets require a unique ID var, and a window tint button (`/obj/machinery/button/windowtint`) with a matching ID var. The default `range` of the button is 7 tiles but can be amended with a var edit. - - Tiny fans (`/obj/structure/fans/tiny`) can be used to block airflow into problematic areas, but are not a substitute for proper door and firelock combinations. They are useful under blast doors that lead to space when opened. - - Firelocks should be used at area boundaries over doors and windoors, but not windows. Firelocks can also be used to break up hallways at reasonable intervals. - - Double firelocks are not permitted. - - Maintenance access doors should never have firelocks placed over them. - - Windows to secure areas or external areas should be reinforced. Windows in engine areas should be reinforced plasma glass. - - Windows in high security areas, such as the brig, bridge, and head of staff offices, should be electrified by placing a wire node under the window. - - Lights are to be used sparingly, they draw a significant amount of power. - - Ensure door and windoor access is correctly set, this is now done by using access helpers. - - - Multiple accesses can be added to a door by placing multiple access helpers on the same tile. Be sure to pay attention so as to avoid mixing up `all` and `any` subtypes. - - Old doors that use var edited access should be updated to use the correct access helper, and the var edit on the door should be cleaned. - - See [`code\modules\mapping\access_helpers.dm`](../code/modules/mapping/access_helpers.dm) for a list of all access helpers. - - Subtypes of `/obj/effect/mapping_helpers/airlock/access/any` lets anyone with ONE OF THE LISTED ACCESSES open the door. - - Subtypes of `/obj/effect/mapping_helpers/airlock/access/all` requires ALL ACCESSES present to open the door. - - - Departments should be connected to maintenance through a back or side door. This lets players escape and allows antags to break in. - - If this is not possible, departments should have extra entry and exit points. - - Engine areas, or areas with a high probability of receiving explosions, should use reinforced flooring if appropriate. - - External areas, or areas where depressurisation is expected and normal, should use airless turf variants to prevent additional atmospherics load. - - Edits in mapping tools should almost always be possible to replicate in-game. For this reason, avoid stacking multiple structures on the same tile (i.e. placing a light and an APC on the same wall.) - -### Other Notes - -- Bloated code may be necessary to add a certain feature, which means there has to be a judgement over whether the feature is worth having or not. You can help make this decision easier by making sure your code is modular. - -- You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs. - -- If you used regex to replace code during development of your code, post the regex in your PR for the benefit of future developers and downstream users. - -- All new var/proc names should use the American English spelling of words. This is for consistency with BYOND. - -- All mentions of the company "Nanotrasen" should be written as such - 'Nanotrasen'. Use of CamelCase (NanoTrasen) is no longer proper. - -- If you are making a PR that adds a config option to change existing behaviour, said config option must default to as close to as current behaviour as possible. - -### Dream Maker Quirks/Tricks - -Like all languages, Dream Maker has its quirks, some of them are beneficial to us, like these: - -#### In-To for-loops - -`for(var/i = 1, i <= some_value, i++)` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but DM's `for(var/i in 1 to some_value)` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. (Note, the `to` keyword is inclusive, so it automatically defaults to replacing `<=`; if you want `<` then you should write it as `1 to some_value-1`). - -HOWEVER, if either `some_value` or `i` changes within the body of the for (underneath the `for(...)` header) or if you are looping over a list AND changing the length of the list then you can NOT use this type of for-loop! - -### `for(var/A in list)` VS `for(var/i in 1 to length(list))` - -The former is faster than the latter, as shown by the following profile results: [https://file.house/zy7H.png](https://file.house/zy7H.png) - -Code used for the test in a readable format: [https://pastebin.com/w50uERkG](https://pastebin.com/w50uERkG) - -#### Istypeless for loops - -A name for a differing syntax for writing for-each style loops in DM. It's NOT DM's standard syntax, hence why this is considered a quirk. Take a look at this: - -```dm -var/list/bag_of_items = list(sword1, apple, coinpouch, sword2, sword3) -var/obj/item/sword/best_sword -for(var/obj/item/sword/S in bag_of_items) - if(!best_sword || S.damage > best_sword.damage) - best_sword = S -``` - -The above is a simple proc for checking all swords in a container and returning the one with the highest damage, and it uses DM's standard syntax for a for-loop by specifying a type in the variable of the for's header that DM interprets as a type to filter by. It performs this filter using `istype()` (or some internal-magic similar to `istype()` - this is BYOND, after all). This is fine in its current state for `bag_of_items`, but if `bag_of_items` contained ONLY swords, or only SUBTYPES of swords, then the above is inefficient. For example: - -```dm -var/list/bag_of_swords = list(sword1, sword2, sword3, sword4) -var/obj/item/sword/best_sword -for(var/obj/item/sword/S in bag_of_swords) - if(!best_sword || S.damage > best_sword.damage) - best_sword = S -``` - -The above code specifies a type for DM to filter by. - -With the previous example that's perfectly fine, we only want swords, but if the bag only contains swords? Is DM still going to try to filter because we gave it a type to filter by? YES, and here comes the inefficiency. Wherever a list (or other container, such as an atom (in which case you're technically accessing their special contents list, but that's irrelevant)) contains datums of the same datatype or subtypes of the datatype you require for your loop's body, you can circumvent DM's filtering and automatic `istype()` checks by writing the loop as such: - -```dm -var/list/bag_of_swords = list(sword, sword, sword, sword) -var/obj/item/sword/best_sword -for(var/s in bag_of_swords) - var/obj/item/sword/S = s - if(!best_sword || S.damage > best_sword.damage) - best_sword = S -``` - -Of course, if the list contains data of a mixed type then the above optimisation is DANGEROUS, as it will blindly typecast all data in the list as the -specified type, even if it isn't really that type, causing runtime errors (AKA your shit won't work if this happens). - -#### Dot variable - -Like other languages in the C family, DM has a `.` or "Dot" operator, used for accessing variables/members/functions of an object instance. eg: - -```dm -var/mob/living/carbon/human/H = YOU_THE_READER -H.gib() -``` - -However, DM also has a dot _variable_, accessed just as `.` on its own, defaulting to a value of null. Now, what's special about the dot operator is that it is automatically returned (as in the `return` statement) at the end of a proc, provided the proc does not already manually return (`return count` for example.) Why is this special? - -With `.` being everpresent in every proc, can we use it as a temporary variable? Of course we can! However, the `.` operator cannot replace a typecasted variable - it can hold data any other var in DM can, it just can't be accessed as one, although the `.` operator is compatible with a few operators that look weird but work perfectly fine, such as: `.++` for incrementing `.'s` value, or `.[1]` for accessing the first element of `.`, provided that it's a list. - -## Globals versus static - -DM has a var keyword, called global. This var keyword is for vars inside of types. For instance: - -```dm -/mob - var/global/thing = TRUE -``` - -This does NOT mean that you can access it everywhere like a global var. Instead, it means that that var will only exist once for all instances of its type, in this case that var will only exist once for all mobs - it's shared across everything in its type. (Much more like the keyword `static` in other languages like PHP/C++/C#/Java) - -Isn't that confusing? - -There is also an undocumented keyword called `static` that has the same behaviour as global but more correctly describes BYOND's behaviour. Therefore, we always use static instead of global where we need it, as it reduces suprise when reading BYOND code. - -### Global Vars - -All new global vars must use the defines in [`code/__DEFINES/_globals.dm`](../code/__DEFINES/_globals.dm). Basic usage is as follows: - -To declare a global var: - -```dm -GLOBAL_VAR(my_global_here) -``` - -To access it: - -```dm -GLOB.my_global_here = X -``` - -There are a few other defines that do other things. `GLOBAL_REAL` shouldn't be used unless you know exactly what you're doing. -`GLOBAL_VAR_INIT` allows you to set an initial value on the var, like `GLOBAL_VAR_INIT(number_one, 1)`. -`GLOBAL_LIST_INIT` allows you to define a list global var with an initial value. Etc. - -### GitHub Staff - -There are 3 roles on the GitHub, these are: - -- Headcoder -- Commit Access -- Review Team - -Each role inherits the lower role's responsibilities (IE: Headcoders also have commit access, and members of commit access are also part of the review team) - -`Headcoders` are the overarching "administrators" of the repository. People included in this role are: - -- [farie82](https://github.com/farie82) -- [S34N](https://github.com/S34NW) -- [SteelSlayer](https://github.com/SteelSlayer) - ---- - -`Commit Access` members have write access to the repository and can merge your PRs. People included in this role are: - -- [AffectedArc07](https://github.com/AffectedArc07) -- [Burzah](https://github.com/Burzah) -- [Charliminator](https://github.com/hal9000PR) -- [Contrabang](https://github.com/Contrabang) -- [DGamerL](https://github.com/DGamerL) -- [lewcc](https://github.com/lewcc) - ---- - -`Review Team` members are people who are denoted as having reviews which can affect mergeability status. People included in this role are: - -- [Burzah](https://github.com/Burzah) -- [Charliminator](https://github.com/hal9000PR) -- [Contrabang](https://github.com/Contrabang) -- [DGamerL](https://github.com/DGamerL) -- [FunnyMan3595](https://github.com/FunnyMan3595) -- [Henri215](https://github.com/Henri215) -- [lewcc](https://github.com/lewcc) -- [Sirryan2002](https://github.com/Sirryan2002) -- [Warriorstar](https://github.com/warriorstar-orion) - ---- - -Full information on the GitHub contribution workflow & policy can be found at [https://www.paradisestation.org/dev/policy/](https://www.paradisestation.org/dev/policy/) - -### PR Status - -Status of your pull request will be communicated via PR labels. This includes: - -- `Status: Awaiting type assignment` - This will be displayed when your PR is awaiting an internal type assignment (for Fix, Balance, Tweak, etc) -- `Status: Awaiting approval` - This will be displayed if your PR is waiting for approval from the specific party, be it Balance or Design. Fixes & Refactors should never have this label -- `Status: Awaiting review` - This will be displayed when your PR has passed the design vote and is now waiting for someone in the review team to approve it -- `Status: Awaiting merge` - Your PR is done and is waiting for someone with commit access to merge it. **Note: Your PR may be delayed if it is pending testmerge or in the mapping queue** diff --git a/.github/DOWNLOADING.md b/.github/DOWNLOADING.md deleted file mode 100644 index 5787b807775c..000000000000 --- a/.github/DOWNLOADING.md +++ /dev/null @@ -1,105 +0,0 @@ -# Info - -This document contains all the relevant information for downloading and running your own ParaCode server. - -## GETTING THE CODE - -The simplest way to obtain the code is using the github .zip feature. - -Click [here](https://github.com/ParadiseSS13/Paradise/archive/master.zip) to get the latest code as a .zip file, then unzip it to wherever you want. - -The more complicated and easier to update method is using git. -You'll need to download git or some client from [here](http://git-scm.com/). -When that's installed, right click in any folder and click on "Git Bash". -When that opens, type in: - -```sh - git clone https://github.com/ParadiseSS13/Paradise.git --depth 1 -``` - -(hint: hold down ctrl and press insert to paste into git bash) - -This will take a while to download (it is the entire repo + history, not just a snapshot), but it provides an easier method for updating. - -## INSTALLATION - -First-time installation should be fairly straightforward. -First, you'll need BYOND installed. We're going to assume you already did this - -This is a sourcecode-only release, so the next step is to compile the server files. -Open paradise.dme by double-clicking it, open the Build menu, and click compile. -This'll take a little while, and if everything's done right, -you'll get a message like this: - -```sh - saving paradise.dmb (DEBUG mode) - paradise.dmb - 0 errors, 0 warnings -``` - -If you see any errors or warnings, -something has gone wrong - possibly a corrupt download or the files extracted wrong, -or a code issue on the main repo. Feel free to ask on Discord. - -Once that's done, open up the config folder. -Firstly, you will want to copy `config.toml` from the example folder into the regular config folder. -You'll want to edit the `url_configuration` section of `config.toml` to set `reboot_url` to your server location, -so that all your players don't get disconnected at the end of each round. -It's recommended you don't turn on the gamemodes with probability 0, -as they have various issues and aren't currently being tested, -so they may have unknown and bizarre bugs. - -You'll also want to edit the `admin_configuration` section of `config.toml` to remove the default admins and add your own. -If you are connecting from localhost to your own test server, you should automatically be admin. -"Head of Staff" is the highest level of access, and the other recommended admin levels for now are -"Game Admin". The format is: - -```toml -# Note that your ranks must be cased properly, usernames can be normal keys or ckey -admin_assignments = [ - {ckey = "Admin1", rank = "Hosting Provider"}, - {ckey = "Admin2", rank = "Game Admin"}, -] -``` - -You can define your own ranks in the admin section of `config.toml`. - -If you want to run a production scale server, we highly recommend using database administrators. - -Finally, to start the server, -run Dream Daemon and enter the path to your compiled paradise.dmb file. -Make sure to set the port to the one you specified in the config.txt, -and set the Security box to 'Trusted'. -Then press GO and the server should start up and be ready to join. - -## Installation (Linux) - -The code is fully able to run on linux, however windows is still the recommended platform. The libraries we use for external functions (RUSTG and MILLA) require some extra dependencies. - -For debian, please download the latest RUSTG release from [https://github.com/ParadiseSS13/rust-g](https://github.com/ParadiseSS13/rust-g), run the following: `apt-get install libssl-dev:i386 pkg-config:i386 zlib1g-dev:i386`. - -After installing these packages, RUSTG should be able to build and function as intended. Build instructions are on the RUSTG page. We assume that if you are hosting on linux, you know what you are doing. - -Once you've built RUSTG, you can build MILLA similarly, just go into the `milla/` directory and run `cargo build --release --target=i686-unknown-linux-gnu`. - -## UPDATING - -If you used the zip method, -you'll need to download the zip file again and unzip it somewhere else, -and then copy the `config` and `data` folders over. - -If you used the git method, you simply need to type this in to git bash: - -```sh - git pull --depth 1 -``` - -When you have done this, you'll need to recompile the code, but then it should work fine and be up to date with the live server. - -## SQL Setup - -The SQL backend is required for storing character saves, preferences, administrative data, and many other things. -We recommend running a database if your server is going to be used as more than just a local test server. -Your SQL server details go in the `database_configuration` section of `config.toml`, -and the SQL schema can be found in `SQL/paradise_schema.sql`. -More detailed setup instructions are located on our wiki: -https://www.paradisestation.org/wiki/index.php/Setting_up_the_Database diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f4dbad5742aa..5a0d21b14aac 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -25,7 +25,7 @@ ### Declaration -- [ ] I confirm that I either do not require [pre-approval](https://github.com/ParadiseSS13/Paradise/blob/master/CODE_OF_CONDUCT.md#types-of-changes-that-need-approval) for this PR, or I have obtained such approval and have included a screenshot to demonstrate this below. +- [ ] I confirm that I either do not require [pre-approval](https://github.com/ParadiseSS13/Paradise/blob/master/docs/CODE_OF_CONDUCT.md#types-of-changes-that-need-approval) for this PR, or I have obtained such approval and have included a screenshot to demonstrate this below.
diff --git a/.github/SETTING_UP_GIT.md b/.github/SETTING_UP_GIT.md deleted file mode 100644 index 4eca3d017cb2..000000000000 --- a/.github/SETTING_UP_GIT.md +++ /dev/null @@ -1,178 +0,0 @@ -# Setting Up Git (Windows/Mac Only) - -## Linux users - -You will want to install the `git` package from your distribution's respective -package manager, whatever that may be. - ---- - -## Windows user -## Git-SCM -So you want to start contributing to Paradise? Where, well do you start? -First off, you will need some tools to work with Git, the -[Version Control System](https://en.wikipedia.org/wiki/Version_control) -that we operate off. There are many choices out there for dealing with Git, -including GitHub for Desktop- However, all of them are based off of the same -command-line tools. As such, we advise, rather than using a GUI Program, you -learn how to use the command line. - -An important note here is that the Git-SCM package for Windows includes -some GUI-based software in it. This can be used for easily monitoring the -state of your current branch, without downloading multiple different tools. - -## Installing -The version of Git that we will be using is available at https://git-scm.com/. -There should be four big orange buttons on the front page of the site when you -go there. You will want to click on the one labeled "Downloads". -![https://i.imgur.com/a6tX7IV.png](https://i.imgur.com/a6tX7IV.png) - -From here, you will want to select your operating system in this box. -![https://i.imgur.com/Ee4wVsF.png](https://i.imgur.com/Ee4wVsF.png) - -Download the `setup` version, which should automatically start downloading when -you select your operating system. Place it wherever you prefer to store your -downloaded files. You should end up with a file that looks like -`Git-version.number.here-32/64-bit.exe`. You should run this executable file. -![https://i.imgur.com/jnbodzV.png](https://i.imgur.com/jnbodzV.png) - -Click Next, after reading the GNU-GPL license if you wish to do so, which will -bring you to this screen. -![https://i.imgur.com/cl9RodU.png](https://i.imgur.com/cl9RodU.png) - -Your default options may be different than this- You'll want to amend them to -match this screenshot. (Future proofing: `Windows Explorer integration` partially -selected, just for `Git Bash Here`, `Git LFS (Large File Support)` checked, -and `Associate .git* configuration files with the default text editor` checked. -All other boxes should be left unchecked). Click next. The next screen is very -important. -![https://i.imgur.com/6ii7aRO.png](https://i.imgur.com/6ii7aRO.png) - -The screen should say `Adjusting your PATH environment`. You will definitely want -to select `Use Git from Git Bash only`- This is the safest option, and will not -change your PATH variables at all. The disadvantage of this is that any future -terminal emulators will be unable to use Git, as will the windows command prompt. - -Select `Use the OpenSSL library` for `Choosing HTTPS transport backend`. - -For Windows, you will also get the following screen: -![https://i.imgur.com/jOZJWvO.png](https://i.imgur.com/jOZJWvO.png) - -You will want to select "Checkout Windows-style, commit Unix-style line endings" -for working with our repository. - -If you get the choice between MinTTY and Windows' default console window, select -MinTTY. -![https://i.imgur.com/ZdZU0NB.png](https://i.imgur.com/ZdZU0NB.png) - -For `configuring extra options`, select `Enable file system caching` and -`Enable Git Credential Manager`, leaving `Enable symbolic links` disabled. -![https://i.imgur.com/6gspQAL.png](https://i.imgur.com/6gspQAL.png) - -From there, just hit `Install`. - -## Configuring - -We are going to configure Git for password-less SSH authentication for GitHub. -There is a simple way to make this require that you instead just enter your -password once upon opening a terminal for the first time running the program -after a restart, which we will cover when it is applicable. - -This guide is mostly copy-pasted from -`https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/`, - So you can view that for further instruction. - -As we are working with a completely fresh install of Git, we can skip over some -steps. You will want to open Git Bash, then use the text below, changing the email -to the one you use for GitHub. -```bash -ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -``` -This will create a new SSH private/public key, using the email as a label. -```bash -Generating public/private rsa key pair. -``` - -When you're prompted to `Enter a file in which to save the key`, you will want -to just press enter, to accept the default file location. We will rename the key -later. - -Remember that I mentioned you could either do password-less SSH authentication, -or require a password one time per session? This is the point at which you -choose this, with the `Enter passphrase (empty for no passphrase):` prompt. If -you do not care about having a password on the private key (which you should -never ever distribute beyond your own computers on secure mediums), you can just -hit enter twice to completely skip setting a password on the key. Otherwise, enter -your desired password. - -You will now want to go to your home directory `C:/Users/YourName` and open the -`.ssh` folder. You should see two files in here, `id_rsa` and `id_rsa.pub`. Rename -them both to `your_github_username_rsa` and `your_github_username_rsa.pub`. -You'll then want to create two new folders, `public` and `private`. Put the `.pub` -file into `public`, then put the other one into `private`. - -As we are not using GitHub for Desktop, we will need to now setup SSH-agent. -Start by typing the command `vim ~/.bashrc`. This will open the Vi IMproved text -editor, which is one of the ones that comes with Git by default. It is entirely -built into the shell, and the interface will take a bit of getting used to. -You will want to hit `i`, to go into `Insert Mode`. This will produce a blinking -cursor on the top section, what you would expect for a text editor. You may now -type what you like, or right click and paste to paste from your clipboard. -Paste the following into the file: -```bash -#!/bin/bash - -SSH_ENV=$HOME/.ssh/environment - -function add_keys { - ssh-add ~/.ssh/private/* -} - -function start_agent { - echo "Initializing new SSH agent..." - /usr/bin/ssh-agent | sed 's/^echo/#echo' > ${SSH_ENV} - echo Suceeded - chmod 600 ${SSH_ENV} - . ${SSH_ENV} > /dev/null - add_keys; -} - -# Source SSH settings, if applicable - -if [ -f "${SSH_ENV}" ]; then - . ${SSH_ENV} > /dev/null - ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || { - start_agent; - } -else - start_agent; -fi -``` - -Hit escape, then hit `:` followed by `wq` and enter. This is, to Vi, Insert Mode -to command mode (`ESC`), Command start `:`, and `w`rite `q`uit `ENTER` submit. - -Restart Git Bash- If you set a password on your SSH key, it will ask it for you -when it starts. Otherwise, it should just say `Identity added: ...`. You can see -all of the identities you have by running `ssh-add -l` in the shell at any time. - -You will now want to follow the instructions in this article to add the SSH key -to your GitHub profile: -https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/ - -Obviously, for the first step, use the key we renamed and put into the `public` -folder, not the one in the `private` folder. - -To test that this all worked, you should run `ssh -T git@github.com`. If there -are any warning messages about authenticity, type `yes` followed by enter. You -should then see: -``` -Hi username! You've successfully authenticated, but GitHub does not -provide shell access. -``` - -If not, follow the troubleshooting directions in this article: -https://help.github.com/articles/error-permission-denied-publickey/ - - -Congratulations, you have now setup Git for Windows. \ No newline at end of file diff --git a/.github/TICK_ORDER.md b/.github/TICK_ORDER.md deleted file mode 100644 index c6e27741e2c8..000000000000 --- a/.github/TICK_ORDER.md +++ /dev/null @@ -1,21 +0,0 @@ -The byond tick proceeds as follows: -1. procs sleeping via walk() are resumed (I don't know why these are first) - -2. normal sleeping procs are resumed, in the order they went to sleep in the first place, this is where the MC wakes up and processes subsystems. a consequence of this is that the MC almost never resumes before other sleeping procs, because it only goes to sleep for 1 tick 99% of the time, and 99% of procs either go to sleep for less time than the MC (which guarantees that they entered the sleep queue earlier when its time to wake up) and/or were called synchronously from the MC's execution, almost all of the time the MC is the last sleeping proc to resume in any given tick. This is good because it means the MC can account for the cost of previous resuming procs in the tick, and minimizes overtime. - -3. control is passed to byond after all of our code's procs stop execution for this tick - -4. a few small things happen in byond internals - -5. SendMaps is called for this tick, which processes the game state for all clients connected to the game and handles sending them changes -in appearances within their view range. This is expensive and takes up a significant portion of our tick, about 0.45% per connected player -as of 3/20/2022. meaning that with 50 players, 22.5% of our tick is being used up by just SendMaps, after all of our code has stopped executing. That's only the average across all rounds, for most high-pop rounds it can look like 0.6% of the tick per player, which is 30% for 50 players. - -6. After SendMaps ends, client verbs sent to the server are executed, and its the last major step before the next tick begins. -During the course of the tick, a client can send a command to the server saying that they have executed any verb. The actual code defined -for that /verb/name() proc isnt executed until this point, and the way the MC is designed makes this especially likely to make verbs -"overrun" the bounds of the tick they executed in, stopping the other tick from starting and thus delaying the MC firing in that tick. - -The master controller can derive how much of the tick was used in: procs executing before it woke up (because of world.tick_usage), and SendMaps (because of world.map_cpu, since this is a running average you cant derive the tick spent on maptick on any particular tick). It cannot derive how much of the tick was used for sleeping procs resuming after the MC ran, or for verbs executing after SendMaps. - -It is for these reasons why you should heavily limit processing done in verbs, while procs resuming after the MC are rare, verbs are not, and are much more likely to cause overtime since they're literally at the end of the tick. If you make a verb, try to offload any expensive work to the beginning of the next tick via a verb management subsystem. diff --git a/.github/workflows/devdocs.yml b/.github/workflows/devdocs.yml new file mode 100644 index 000000000000..515d6f5bee7a --- /dev/null +++ b/.github/workflows/devdocs.yml @@ -0,0 +1,29 @@ +name: Deploy Devdocs + +on: + push: + paths: + - 'docs/**' + +jobs: + build_and_deploy: + runs-on: ubuntu-latest + name: Build and Deploy + steps: + - uses: actions/checkout@v4 + + - name: Build docs + run: | + python -m pip install mkdocs==1.6.0 mkdocs-material==9.5.31 mkdocs-github-admonitions-plugin==0.0.2 + python -m mkdocs build + + - name: Deploy docs + uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} + action: 'upload' + app_location: 'site' + skip_app_build: true + skip_api_build: true + env: + SKIP_DEPLOY_ON_MISSING_SECRETS: true diff --git a/.gitignore b/.gitignore index 6f6c2dd9c369..e1a2a1e9325b 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,6 @@ $RECYCLE.BIN # Rust output. /milla/target/* + +# mkdocs output. +site diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 43c12ed66f8a..000000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,55 +0,0 @@ -# Important - -The Paradise GitHub is not exempt from Paradise Station community and server rules, especially rules 0, 1, and 4. An inability to abide by the rules on the GitHub will result in disciplinary action up to, or including, a repository ban. - -## General Expectations - -### PR Discussion - -Comments on Pull Requests should remain relevant to the PR in question and not derail discussions. - -Under no circumstances are users to be attacked for their ideas or contributions. While constructive criticism is encouraged, toxicity or general mean-spirited behaviour will not be tolerated. All participants on a given PR or issue are expected to be civil. Failure to do so will result in disciplinary action. - -"Merge-nagging" or similar behaviour is not acceptable. Comments of this nature will result in warnings or an outright ban from the repository depending on general behaviour. - -If you exhibit behaviour that's considered to be a net-negative to the community (offensive commentary, repeat violations, constant nagging, personal attacks, etc.), you may be banned from other Paradise services (Discord, forums, server, wiki, etc.) - -Headcoders reserve the right to permanently revoke access from the repository if your behaviour is considered to be a net negative. - -### PR Approval/Objection Info - -Headcoders (who will take into account the votes from the relevant teams) have the final say on Pull Requests. While thumbsup/thumbsdown reaction ratios are generally taken into account, they do not dictate whether or not a PR will be merged. - -After a twenty four hour minimum waiting period, Pull Requests can be merged once they receive approval from the relevant team. An exception is made for refactors and fixes, which may be merged by any member with commit access' discretion with no waiting period. - -While normally provided, voting team members are not obligated to publicly state their objections to a Pull Request. Attacking or berating a voting team member over an objection will not be tolerated. Additionally, whining over the closure of a PR, the existence of an objection, or similar behaviour, will not be tolerated. - -Headcoders may close your PR at their discretion if your PR history has little focus on improving repo maintainability (ie: making nothing but 20 balance or feature PRs). Likewise, balance PRs may be closed if the PR author has little-to-no time played on the server. This is to ensure balance changes are made by people actually in-touch with the server atmosphere. - -### PR Expectations - -Contributors may only have a maximum of **2** feature or balance Pull Requests open at any given time. Any additional Pull Requests beyond this limit will be closed at the discretion of the Headcoders. The Headcoders may grant an exemption to this limit on a case-by-case basis, as the need arises. - -All Pull Requests are expected to be tested prior to submission. If a submitted Pull Request fails to pass CI checks, the likelihood of it being merged will be significantly lower. If you can't take the time to compile/test your Pull Request, do not expect a warm reception. - -Barring highly specific circumstances (such as single line changes, submissions from advanced users, or changes to repo documentation), we will not accept Pull Requests utilising the web editor. - -Pull Requests regarding heavy-handed nerfs, particularly immediately after said mechanic was used, will be tagged with `I ded pls nerf`. A bad experience with a particular mechanic is not a justification for nerfing it. - -Reactionary revert PRs are not tolerated under any circumstances. Posting a revert immediately after a Pull Request is merged will result in a repoban. - -It is expected that contributors discuss larger changes on the [Paradise Station forums](https://www.paradisestation.org/forum/91-code-discussion/), [GitHub discussions tab](https://github.com/ParadiseSS13/Paradise/discussions), or the [Discord project-discussion forum](https://discord.com/channels/145533722026967040/1110966752898207824) prior to starting work on a Pull Request. The amount of time spent on any given Pull Request is not relevant. Repo staff are not responsible for contributors wasting their time creating features nobody asked for. Be sure to inform the corresponding teams about the forum post or discussion. - -For changes to content listed below, contributors **must** obtain approval from a headcoder or a member of either the balance, design, mapping, or sprite team (depending on which teams are relevant to the changes) before opening their Pull Request. This approval must be displayed in the Pull Request description body in the form of a screenshot. The Headcoders may grant an exemption to this requirement on a case-by-case basis, as the need arises. - - - - -> [!IMPORTANT] -> ### Currently, changes to the following types of content requires pre-approval: -> - **Security content (excluding fixes, code improvement, refactors, sprites, and mapping changes)** -> - **Antagonist content (excluding fixes, code improvement, refactors, sprites, and mapping changes)** -> - **Species content (excluding fixes, code improvement, and refactors)** -> - **Large changes (for example PRs that touch multiple systems, many files, many lines of code)** -> - **Changes that might be controversial** -> - **Changes with wide-ranging balance or design implications** diff --git a/README.md b/README.md index 4976414fc67e..666b84e5f44f 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ > [!TIP] > Want to contribute for the first time but unsure where to start?
> Join our Discord and check out the [#coding_chat](https://discord.com/channels/145533722026967040/145700319819464704) channel for helpful links and advice!
-> Alternatively, have a look at our community maintained [Guide to Contributing](https://paradisestation.org/wiki/index.php?title=Guide_to_Contributing) +> Alternatively, have a look at our community maintained [Getting Started Guide](./docs/contributing/getting_started.md) # Useful Documents and Links @@ -64,15 +64,15 @@ This reference site by the creators of BYOND details information on the DM language, the syntax used, functionality of native procs, and a lot more. This is always useful to have on hand when contributing. -- ### [Autodocumentation Guide](.github/AUTODOC_GUIDE.md) +- ### [Autodocumentation Guide](./docs/references/autodoc.md) This guide shows you how to leave code comments that comply with "autodocumentation", a system designed to make everyone's lives easier when reading or reviewing code! -- ### [Code of Conduct](./CODE_OF_CONDUCT.md) +- ### [Code of Conduct](./docs/CODE_OF_CONDUCT.md) All contributors are expected to read our Code of Conduct before they take part in our community. -- ### [Contribution Guide](.github/CONTRIBUTING.md) +- ### [Contribution Guide](./docs/CONTRIBUTING.md) Not sure how to take part and contribute? This guide gives an overview of how to make comments, pull requests, and open issues. diff --git a/_maps/map_files/RandomRuins/SpaceRuins/abandoned_engi_sat.dmm b/_maps/map_files/RandomRuins/SpaceRuins/abandoned_engi_sat.dmm index 17b1b003e196..fc8f789a5413 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/abandoned_engi_sat.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/abandoned_engi_sat.dmm @@ -517,10 +517,7 @@ }, /area/ruin/space/abandoned_engi_sat) "oG" = ( -/obj/structure/closet{ - icon_state = "atmos_wardrobe"; - name = "atmospherics wardrobe" - }, +/obj/structure/closet/wardrobe/atmospherics_yellow/empty, /obj/machinery/light_switch{ dir = 1; pixel_y = -24; @@ -570,11 +567,7 @@ }, /area/ruin/space/abandoned_engi_sat) "pX" = ( -/obj/structure/closet{ - icon_state = "eng_secure"; - open_door_sprite = "eng_secure_door"; - name = "engineer's locker" - }, +/obj/structure/closet/secure_closet/engineering_personal/empty, /turf/simulated/floor/wood/airless, /area/ruin/space/abandoned_engi_sat) "qu" = ( @@ -903,11 +896,7 @@ icon_state = "tube-broken"; status = 2 }, -/obj/structure/closet{ - icon_state = "fire"; - name = "fire-safety closet"; - desc = "It's a storage unit for fire-fighting supplies." - }, +/obj/structure/closet/firecloset, /turf/simulated/floor/plasteel{ icon_state = "bot"; dir = 1 @@ -1436,13 +1425,7 @@ /turf/simulated/floor/plating, /area/ruin/space/abandoned_engi_sat) "Yz" = ( -/obj/structure/closet{ - icon_state = "emergency"; - open_door_sprite = "emergency_door"; - name = "emergency closet"; - desc = "It's a storage unit for emergency breathmasks and o2 tanks."; - opened = 1 - }, +/obj/structure/closet/emcloset, /turf/simulated/floor/plasteel{ icon_state = "bot"; dir = 1 diff --git a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm index 2db26731792d..f7d730106c91 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm @@ -349,7 +349,7 @@ }, /obj/structure/rack, /obj/item/melee/baton/cattleprod, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "dark" }, diff --git a/_maps/map_files/RandomRuins/SpaceRuins/casino.dmm b/_maps/map_files/RandomRuins/SpaceRuins/casino.dmm index c12b99680ace..e6788fd772a3 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/casino.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/casino.dmm @@ -633,7 +633,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/powered/casino/security) "pB" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm b/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm index 893390b5251e..4b7c7a3edcda 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm @@ -860,7 +860,7 @@ /area/ruin/space/deepstorage) "cU" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "vault" @@ -1079,7 +1079,7 @@ /area/ruin/space/deepstorage) "eI" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "vault" @@ -1211,7 +1211,7 @@ dir = 1 }, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "podfloor" @@ -1259,7 +1259,7 @@ dir = 4 }, /obj/machinery/light/small, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "vault" @@ -1462,7 +1462,7 @@ dir = 4 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "whiteredcorner"; dir = 1 @@ -1495,7 +1495,7 @@ /area/ruin/space/deepstorage) "hK" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/contraband/power{ pixel_x = -32 }, @@ -1619,7 +1619,7 @@ /obj/structure/window/reinforced/polarized{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "vault" @@ -1742,7 +1742,7 @@ /area/ruin/space/deepstorage) "jq" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/deepstorage) "jr" = ( @@ -1946,7 +1946,7 @@ /obj/structure/window/reinforced/polarized{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/cobweb, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ @@ -1981,7 +1981,7 @@ "kG" = ( /obj/structure/table, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "podfloor" @@ -2093,7 +2093,7 @@ dir = 1 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "vault" @@ -2111,12 +2111,12 @@ dir = 6 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/catwalk, /area/ruin/space/deepstorage) "lL" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -2539,7 +2539,7 @@ dir = 4 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "darkredcorners"; dir = 4 @@ -2593,7 +2593,7 @@ /obj/structure/railing/corner, /obj/structure/rack, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "vault" @@ -2757,7 +2757,7 @@ /area/ruin/space/deepstorage) "qa" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 8 }, @@ -2860,7 +2860,7 @@ /area/ruin/space/deepstorage) "qQ" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -3011,7 +3011,7 @@ /area/ruin/space/deepstorage) "rQ" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "vault" @@ -3019,7 +3019,7 @@ /area/ruin/space/deepstorage) "rS" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -3145,7 +3145,7 @@ /obj/structure/window/reinforced/polarized{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "vault" @@ -3153,7 +3153,7 @@ /area/ruin/space/deepstorage) "sY" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "podfloor" @@ -3169,7 +3169,7 @@ /area/ruin/space/unpowered) "ta" = ( /obj/structure/table/glass, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "whiteredcorner" }, @@ -3348,7 +3348,7 @@ /area/ruin/space/deepstorage) "tH" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ dir = 5; @@ -3409,7 +3409,7 @@ /area/ruin/space/deepstorage) "tZ" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "freezerfloor" }, @@ -4657,7 +4657,7 @@ /area/ruin/space/deepstorage) "Cl" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ dir = 10; @@ -4770,7 +4770,7 @@ "CZ" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "black" @@ -5233,7 +5233,7 @@ /area/ruin/space/unpowered) "GE" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "vault" @@ -5465,7 +5465,7 @@ /area/ruin/space/deepstorage) "IJ" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -5957,7 +5957,7 @@ "Mb" = ( /obj/structure/railing, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/catwalk, /area/ruin/space/deepstorage) "Mf" = ( @@ -6166,7 +6166,7 @@ /area/ruin/space/deepstorage) "Ob" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "black" @@ -6283,7 +6283,7 @@ /obj/structure/window/reinforced/polarized{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ dir = 10; @@ -6412,7 +6412,7 @@ /area/ruin/space/deepstorage) "PU" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 4 }, @@ -6596,7 +6596,7 @@ dir = 10 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/deepstorage) "Rc" = ( @@ -7108,7 +7108,7 @@ /area/ruin/space/unpowered) "UM" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "darkredcorners"; dir = 1 @@ -7529,7 +7529,7 @@ dir = 1 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "vault" @@ -7614,7 +7614,7 @@ /area/ruin/space/deepstorage) "XN" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "vault" @@ -7905,7 +7905,7 @@ "ZS" = ( /obj/effect/decal/cleanable/cobweb2, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/basalt, /area/ruin/space/deepstorage) "ZU" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm b/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm index 1e380195b61d..24116f28c9cb 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm @@ -442,7 +442,7 @@ /area/ruin/space/moonbase19) "bh" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "caution"; dir = 4 @@ -527,7 +527,7 @@ dir = 4 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/catwalk, /area/ruin/space/moonbase19) "bv" = ( @@ -615,7 +615,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/cobweb, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "caution" @@ -678,7 +678,7 @@ /area/ruin/space/moonbase19) "bU" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "dark" @@ -1043,7 +1043,7 @@ dir = 1 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/catwalk, /area/ruin/space/moonbase19) "dg" = ( @@ -1329,7 +1329,7 @@ /area/ruin/space/moonbase19) "eb" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "showroomfloor"; temperature = 273.15 @@ -2165,7 +2165,7 @@ "gX" = ( /obj/structure/rack, /obj/effect/decal/cleanable/cobweb2, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "caution" @@ -3083,7 +3083,7 @@ /obj/effect/decal/cleanable/dirt, /obj/structure/grille/broken, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/moonbase19) "jy" = ( @@ -3316,7 +3316,7 @@ /area/ruin/space/moonbase19) "kf" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "caution" @@ -3783,7 +3783,7 @@ /area/ruin/space/moonbase19) "mb" = ( /obj/structure/closet/cabinet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/carpet, /area/ruin/space/moonbase19) "md" = ( @@ -4416,7 +4416,7 @@ }, /obj/structure/rack, /obj/structure/window/basic, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/moonbase19) "ot" = ( @@ -4509,7 +4509,7 @@ /obj/structure/window/reinforced{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/ruin/space/moonbase19) "oM" = ( @@ -4753,7 +4753,7 @@ "pL" = ( /obj/structure/rack, /obj/effect/decal/cleanable/cobweb2, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/moonbase19) "pN" = ( @@ -6485,7 +6485,7 @@ /area/ruin/space/moonbase19) "xw" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/ruin/space/moonbase19) "xz" = ( @@ -6864,7 +6864,7 @@ "yZ" = ( /obj/structure/closet/cabinet, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/carpet, /area/ruin/space/moonbase19) "zh" = ( @@ -7084,7 +7084,7 @@ "zW" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/moonbase19) "Aa" = ( @@ -7698,7 +7698,7 @@ "CH" = ( /obj/structure/rack, /obj/effect/decal/cleanable/cobweb2, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/ruin/space/moonbase19) "CI" = ( @@ -7893,7 +7893,7 @@ /area/ruin/space/moonbase19) "Dw" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -7962,7 +7962,7 @@ /obj/machinery/light/small{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/ruin/space/moonbase19) "DO" = ( @@ -8370,7 +8370,7 @@ /area/ruin/space/moonbase19) "FC" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "caution"; dir = 10 @@ -8424,7 +8424,7 @@ /area/ruin/space/moonbase19) "FT" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitecorner" @@ -8877,7 +8877,7 @@ /area/ruin/space/moonbase19) "HV" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ icon_state = "yellowsiding"; @@ -10248,7 +10248,7 @@ /area/ruin/space/moonbase19) "Of" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/ruin/space/moonbase19) "Og" = ( @@ -10839,7 +10839,7 @@ /obj/structure/sign/pods{ pixel_y = 32 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/catwalk, /area/ruin/space/moonbase19) "QG" = ( @@ -11045,7 +11045,7 @@ /area/ruin/space/moonbase19) "Rv" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -11858,7 +11858,7 @@ "Vo" = ( /obj/structure/closet/crate/can, /obj/effect/decal/cleanable/cobweb2, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "escape" @@ -12160,7 +12160,7 @@ /turf/simulated/floor/catwalk, /area/ruin/space/moonbase19) "WH" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/table, /turf/simulated/floor/plasteel{ icon_state = "whitecorner"; @@ -12502,7 +12502,7 @@ /area/ruin/space/moonbase19) "XX" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "cafeteria" @@ -12830,7 +12830,7 @@ }, /area/ruin/space/moonbase19) "Zp" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/table, /turf/simulated/floor/plasteel{ dir = 10; diff --git a/_maps/map_files/RandomRuins/SpaceRuins/rocky_motel.dmm b/_maps/map_files/RandomRuins/SpaceRuins/rocky_motel.dmm index 73ad23907b0c..cded6ef453ce 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/rocky_motel.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/rocky_motel.dmm @@ -451,7 +451,6 @@ "Ub" = ( /obj/item/reagent_containers/glass/bucket, /obj/item/mop, -/obj/item/malfbroom, /turf/simulated/floor/plasteel{ icon_state = "freezerfloor" }, diff --git a/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm b/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm index 3feac9757b90..d1560fdd8189 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm @@ -5,6 +5,7 @@ d2 = 2; icon_state = "1-2" }, +/obj/machinery/floodlight, /turf/simulated/floor/bluegrid{ name = "Mainframe Base"; nitrogen = 400; @@ -218,11 +219,6 @@ "dD" = ( /obj/structure/closet/crate, /obj/item/multitool, -/obj/machinery/camera{ - c_tag = "Telecomms Entrance South"; - dir = 1; - network = list("Telecomms") - }, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms/tele) "dR" = ( @@ -268,6 +264,9 @@ }, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms/computer) +"eA" = ( +/turf/simulated/wall/indestructible/riveted, +/area/ruin/space/telecomms/foyer) "eE" = ( /obj/machinery/mecha_part_fabricator, /turf/simulated/floor/plasteel{ @@ -438,7 +437,7 @@ pixel_x = 29; pixel_y = 0 }, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms) "hq" = ( @@ -699,7 +698,7 @@ /area/space/nearstation) "mN" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms) "mO" = ( @@ -710,7 +709,7 @@ pixel_x = 0 }, /obj/structure/grille, -/turf/simulated/floor/plating/airless, +/turf/simulated/floor/catwalk/airless, /area/space/nearstation) "mZ" = ( /obj/machinery/door/airlock/maintenance_hatch{ @@ -718,6 +717,10 @@ locked = 1 }, /obj/machinery/door/firedoor, +/obj/machinery/door/poddoor{ + id = "dvorak"; + id_tag = "dvorak" + }, /turf/simulated/floor/plasteel{ icon_state = "vault"; dir = 5 @@ -1010,7 +1013,7 @@ /obj/machinery/light_switch{ pixel_y = -28 }, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms) "qO" = ( @@ -1211,7 +1214,7 @@ /turf/simulated/floor/plasteel, /area/ruin/space/telecomms/computer) "tW" = ( -/obj/machinery/r_n_d/server/upgraded, +/obj/machinery/power/grounding_rod, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "vault"; @@ -1512,6 +1515,10 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/door/firedoor, +/obj/machinery/door/poddoor{ + id = "dvorak"; + id_tag = "dvorak" + }, /turf/simulated/floor/plasteel{ icon_state = "vault"; dir = 5 @@ -1645,17 +1652,6 @@ /obj/item/clothing/glasses/night, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms/tele) -"zg" = ( -/obj/machinery/atmospherics/pipe/simple/visible{ - dir = 4 - }, -/obj/machinery/camera{ - c_tag = "Telecomms Power Control"; - dir = 2; - network = list("Telecomms") - }, -/turf/simulated/floor/catwalk, -/area/ruin/space/telecomms/powercontrol) "zk" = ( /obj/structure/lattice, /obj/effect/abstract/cheese_trap, @@ -1962,6 +1958,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 6 }, +/obj/machinery/computer/nonfunctional, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms/foyer) "Dy" = ( @@ -2345,7 +2342,7 @@ /area/space/nearstation) "Jb" = ( /obj/effect/spawner/random_spawners/fungus_maybe, -/turf/simulated/wall/r_wall, +/turf/simulated/wall/indestructible/riveted, /area/ruin/space/telecomms/foyer) "Jm" = ( /obj/machinery/computer/sm_monitor{ @@ -2478,6 +2475,9 @@ temperature = 80 }, /area/ruin/space/telecomms/chamber) +"Lk" = ( +/turf/simulated/wall/indestructible/riveted, +/area/ruin/space/telecomms/tele) "Ln" = ( /obj/structure/window/reinforced{ dir = 4 @@ -2507,6 +2507,17 @@ temperature = 80 }, /area/ruin/space/telecomms/chamber) +"LC" = ( +/obj/machinery/doppler_array, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "vault"; + name = "Mainframe floor"; + nitrogen = 400; + oxygen = 0; + temperature = 80 + }, +/area/ruin/space/telecomms/chamber) "LK" = ( /obj/structure/cable{ d1 = 4; @@ -2563,6 +2574,7 @@ /obj/machinery/light/small{ dir = 1 }, +/obj/effect/abstract/bot_trap, /turf/simulated/floor/plasteel{ icon_state = "vault"; dir = 5 @@ -2745,6 +2757,10 @@ req_access_txt = "61" }, /obj/machinery/door/firedoor, +/obj/machinery/door/poddoor/preopen{ + id = "dvorak"; + id_tag = "dvorak" + }, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms/foyer) "PZ" = ( @@ -2804,6 +2820,12 @@ "Qx" = ( /turf/simulated/floor/catwalk, /area/ruin/space/telecomms/powercontrol) +"Qz" = ( +/obj/machinery/door_control{ + id = "dvorak" + }, +/turf/simulated/wall/indestructible/riveted, +/area/ruin/space/telecomms/chamber) "QN" = ( /obj/structure/window/reinforced{ dir = 4 @@ -2928,7 +2950,7 @@ /obj/item/stock_parts/micro_laser/high, /obj/item/stock_parts/micro_laser/high, /obj/item/stock_parts/micro_laser/high, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/item/stock_parts/matter_bin, /obj/item/stock_parts/matter_bin, /obj/item/stock_parts/matter_bin, @@ -3106,6 +3128,15 @@ temperature = 80 }, /area/ruin/space/telecomms/chamber) +"Vl" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, +/obj/structure/grille, +/turf/simulated/floor/catwalk/airless, +/area/space/nearstation) "Vm" = ( /obj/machinery/light/small, /obj/machinery/light_switch{ @@ -3209,9 +3240,6 @@ dir = 1; level = 1 }, -/obj/item/radio/intercom{ - pixel_y = 25 - }, /turf/simulated/floor/plasteel, /area/ruin/space/telecomms/foyer) "Xf" = ( @@ -6389,7 +6417,7 @@ ik Sv lW Ga -Ga +Vl mh Lp Lp @@ -7123,7 +7151,7 @@ yo FC FC SA -zg +Bn lr Qx PA @@ -7210,7 +7238,7 @@ Zu EO yr Xf -PQ +Lk oG PQ PQ @@ -7296,13 +7324,13 @@ bJ Tw eE il -tW +LC EM Zu uM IL uM -uM +eA yJ BZ PQ @@ -7394,7 +7422,7 @@ Zu Qa nD eJ -uM +eA DV Uw PQ @@ -7578,7 +7606,7 @@ Zu WO nD Ri -uM +eA xv Uw UF @@ -7758,11 +7786,11 @@ gG pa il qe -Zu +Qz Xb oz Ri -uM +eA Rt Uw UF @@ -7854,7 +7882,7 @@ Zu Gt GQ cj -uM +eA Yr VO PQ @@ -7946,7 +7974,7 @@ Zu ao nD Bf -uM +eA iE AY PQ @@ -8017,11 +8045,11 @@ yo Ed xa ik -ik +iD +ux +ux +ux ux -Hr -Hr -Hr To ME Zj @@ -8038,7 +8066,7 @@ Zu uM yD uM -uM +eA HR De PQ @@ -8130,7 +8158,7 @@ Zu EO yr Xf -PQ +Lk oW PQ PQ @@ -8202,12 +8230,12 @@ Dy xa ik ux -Hr +ux Aj -Hr -Hr -Hr -Hr +ux +ux +ux +ux Zu fq EM diff --git a/_maps/map_files/RandomRuins/SpaceRuins/ussp.dmm b/_maps/map_files/RandomRuins/SpaceRuins/ussp.dmm index a2c98e391d9d..4a04d9c2a6d9 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/ussp.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/ussp.dmm @@ -1029,7 +1029,7 @@ /area/ruin/space/derelict/bridge) "cG" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/item/trash/can, /obj/item/trash/semki, /turf/simulated/floor/plasteel{ @@ -1557,7 +1557,7 @@ /area/ruin/space/derelict/arrival) "dT" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/shoes/jackboots, /obj/item/clothing/shoes/jackboots, /obj/item/clothing/shoes/jackboots, @@ -3434,7 +3434,7 @@ /area/ruin/space/derelict/arrival) "iA" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/paper/crumpled{ info = "This note has been crossed out so many times it's hard to make out its contents.
Katerina,

Your eyes are like diamonds,
Forged in the very fire of Cygni-A.
My heart yearns for you,
For your soft beauty
Fuckfucknothat'sbad.

Your voice is like liquid gold,
Smothering me in its delicate contours.
Never before had I known,
The touch,
The touch!
The touch of an angel's voice


note to self: stick to science, poetry is not my forte.
"; name = "love poem" @@ -3561,7 +3561,7 @@ /area/ruin/space/derelict/arrival) "iS" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/contraband/communist_state{ pixel_x = -32 }, @@ -4822,7 +4822,7 @@ /area/ruin/space/derelict/arrival) "lL" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "brown" }, @@ -4894,7 +4894,7 @@ /area/ruin/space/derelict/hallway/primary) "lV" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/spot{ dir = 4 }, @@ -4989,7 +4989,7 @@ "ml" = ( /obj/effect/landmark/damageturf, /obj/structure/grille/broken, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/airless, /area/ruin/space/derelict/hallway/primary) "mn" = ( @@ -6261,7 +6261,7 @@ /area/ruin/space/derelict/crew_quarters) "pE" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/spider/stickyweb, /turf/simulated/floor/plasteel, /area/ruin/space/derelict/crew_quarters) @@ -7128,7 +7128,7 @@ /area/ruin/space/derelict/arrival) "rJ" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/item/trash/popcorn, /obj/item/trash/semki, /obj/item/paper/crumpled{ diff --git a/_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm b/_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm index f03faedabeda..243674063756 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm @@ -680,7 +680,7 @@ /obj/structure/sign/poster/contraband/smoke{ pixel_x = -32 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "caution" @@ -709,7 +709,7 @@ /obj/structure/sign/cargo{ pixel_x = -32 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "titanium"; dir = 4 diff --git a/_maps/map_files/generic/Lavaland.dmm b/_maps/map_files/generic/Lavaland.dmm index ebbcc6a197e5..be524dc396c6 100644 --- a/_maps/map_files/generic/Lavaland.dmm +++ b/_maps/map_files/generic/Lavaland.dmm @@ -4408,7 +4408,7 @@ /obj/structure/closet/cabinet, /obj/item/trash/can, /obj/item/trash/pistachios, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/mask/balaclava, /turf/simulated/floor/wood, /area/mine/laborcamp) @@ -5213,7 +5213,7 @@ /area/lavaland/surface/outdoors) "te" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/cigbutt, /turf/simulated/floor/plasteel{ icon_state = "darkredyellowfull" @@ -5636,7 +5636,7 @@ dir = 4 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/stack/cable_coil, /turf/simulated/floor/plating, /area/mine/laborcamp) @@ -8645,7 +8645,7 @@ "Ow" = ( /obj/effect/turf_decal/delivery/hollow, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/mine/outpost/production) @@ -8917,7 +8917,7 @@ /area/lavaland/surface/outdoors) "Qa" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/stack/marker_beacon/ten, /obj/item/book/random, /turf/simulated/floor/plating, @@ -9743,7 +9743,7 @@ dir = 8; id = "mining_internal" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/mine/outpost/production) "VN" = ( @@ -10351,7 +10351,7 @@ /area/lavaland/surface/outdoors) "YP" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/mounted/frame/apc_frame, /turf/simulated/floor/plating, /area/mine/laborcamp) diff --git a/_maps/map_files/shuttles/emergency_cramped.dmm b/_maps/map_files/shuttles/emergency_cramped.dmm index e1810925cb69..c96a6d1ab9b6 100644 --- a/_maps/map_files/shuttles/emergency_cramped.dmm +++ b/_maps/map_files/shuttles/emergency_cramped.dmm @@ -80,7 +80,7 @@ "r" = ( /obj/effect/turf_decal/delivery/hollow, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/spot{ dir = 4 }, @@ -95,7 +95,7 @@ "u" = ( /obj/effect/turf_decal/delivery/hollow, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/shuttle/escape) "w" = ( diff --git a/_maps/map_files/stations/boxstation.dmm b/_maps/map_files/stations/boxstation.dmm index ad4700d9ec2d..94831feeea42 100644 --- a/_maps/map_files/stations/boxstation.dmm +++ b/_maps/map_files/stations/boxstation.dmm @@ -17,7 +17,7 @@ }, /area/station/science/hallway) "aad" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/cobweb2, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) @@ -4495,7 +4495,7 @@ /obj/item/storage/secure/safe{ pixel_y = 25 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aqb" = ( @@ -6633,7 +6633,7 @@ /area/station/security/processing) "awD" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "awE" = ( @@ -7092,7 +7092,7 @@ dir = 1 }, /obj/item/storage/fancy/donut_box, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "axY" = ( @@ -7126,7 +7126,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "ayf" = ( @@ -8463,7 +8463,7 @@ }, /area/station/legal/magistrate) "aCh" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aCi" = ( @@ -8603,7 +8603,7 @@ /area/station/hallway/primary/fore) "aCz" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aCB" = ( @@ -8909,7 +8909,7 @@ /obj/machinery/light/small{ dir = 8 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aDq" = ( @@ -9673,7 +9673,7 @@ /obj/structure/sign/poster/contraband/random{ pixel_x = -32 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aFN" = ( @@ -9782,7 +9782,7 @@ /turf/simulated/floor/plasteel, /area/station/security/permabrig) "aGb" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aGc" = ( @@ -9800,7 +9800,7 @@ "aGe" = ( /obj/structure/table, /obj/item/restraints/handcuffs/cable, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aGi" = ( @@ -10073,7 +10073,7 @@ /area/station/public/arcade) "aHb" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aHc" = ( @@ -10148,13 +10148,9 @@ /obj/item/melee/baseball_bat, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) -"aHs" = ( -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/fpmaint) "aHt" = ( /obj/structure/table/wood/poker, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/carpet, /area/station/maintenance/fpmaint) "aHu" = ( @@ -10614,7 +10610,7 @@ /area/station/maintenance/fpmaint) "aIV" = ( /obj/effect/spawner/random_spawners/blood_often, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/wood, /area/station/maintenance/fpmaint) "aIW" = ( @@ -10914,7 +10910,7 @@ /area/station/maintenance/fpmaint) "aKa" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) "aKb" = ( @@ -10926,7 +10922,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aKd" = ( -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) "aKf" = ( @@ -11193,7 +11189,7 @@ icon_state = "0-4" }, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/cobweb, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) @@ -11223,7 +11219,7 @@ "aKY" = ( /obj/structure/closet, /obj/item/coin/iron, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aKZ" = ( @@ -11246,7 +11242,7 @@ /area/shuttle/pod_1) "aLc" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aLd" = ( @@ -11411,7 +11407,7 @@ /area/station/service/mime) "aLB" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) "aLC" = ( @@ -11468,7 +11464,7 @@ /area/station/maintenance/fpmaint) "aLM" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aLN" = ( @@ -11704,24 +11700,14 @@ /area/station/maintenance/fpmaint2) "aMi" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) -"aMj" = ( -/obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/fore) "aMl" = ( /obj/structure/table, /obj/item/food/donut, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) -"aMo" = ( -/obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/fsmaint) "aMp" = ( /obj/item/radio/intercom{ name = "east bump"; @@ -12006,11 +11992,6 @@ }, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) -"aNh" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/fsmaint) "aNi" = ( /mob/living/simple_animal/mouse, /turf/simulated/floor/plating, @@ -12128,11 +12109,6 @@ }, /turf/simulated/floor/mineral/titanium/blue, /area/shuttle/pod_2) -"aNA" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/fpmaint2) "aNB" = ( /obj/structure/reagent_dispensers/watertank, /turf/simulated/floor/plating, @@ -12160,11 +12136,6 @@ /obj/machinery/atmospherics/binary/valve, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) -"aNF" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/fpmaint2) "aNH" = ( /obj/structure/chair/stool{ dir = 8 @@ -12581,7 +12552,7 @@ "aOX" = ( /obj/structure/lattice, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /turf/space, /area/space/nearstation) "aOY" = ( @@ -12678,7 +12649,7 @@ "aPu" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "showroomfloor" }, @@ -12933,7 +12904,7 @@ /area/station/maintenance/fpmaint) "aQj" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aQk" = ( @@ -13038,7 +13009,7 @@ /area/station/public/dorms) "aQC" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aQD" = ( @@ -13073,7 +13044,7 @@ }, /area/station/public/dorms) "aQI" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aQJ" = ( @@ -13445,7 +13416,7 @@ /obj/structure/table, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/cable{ d1 = 1; d2 = 2; @@ -13922,7 +13893,7 @@ /obj/effect/turf_decal/delivery/blue/hollow, /obj/effect/decal/cleanable/dirt, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "darkbluefull" }, @@ -14198,7 +14169,7 @@ "aTZ" = ( /obj/structure/closet, /obj/effect/decal/cleanable/cobweb, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aUa" = ( @@ -14207,11 +14178,6 @@ }, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) -"aUb" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/fsmaint) "aUc" = ( /obj/machinery/door/poddoor{ id_tag = "maint2" @@ -14230,7 +14196,7 @@ /obj/structure/closet, /obj/item/reagent_containers/drinks/cans/badminbrew, /obj/effect/landmark/spawner/nukedisc_respawn, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aUf" = ( @@ -14517,7 +14483,7 @@ /obj/structure/chair/stool{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aUV" = ( @@ -15668,7 +15634,7 @@ /area/station/maintenance/fpmaint) "aYb" = ( /obj/item/tank/internals/emergency_oxygen, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/cyan, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) @@ -17086,7 +17052,7 @@ /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "bcB" = ( @@ -18903,7 +18869,7 @@ /area/station/public/storage/office) "bhG" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "darkbluefull" }, @@ -19156,7 +19122,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "bip" = ( @@ -21755,7 +21721,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /turf/simulated/floor/plating, @@ -22623,7 +22589,7 @@ "brF" = ( /obj/structure/rack, /obj/item/melee/baton/cattleprod, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "brG" = ( @@ -22753,7 +22719,7 @@ layer = 2.9 }, /obj/item/extinguisher, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bsd" = ( @@ -22976,7 +22942,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/storage/secure/safe{ pixel_y = 25 }, @@ -23047,7 +23013,7 @@ /area/station/supply/lobby) "bsZ" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, /area/station/maintenance/port) @@ -23419,7 +23385,7 @@ dir = 8; layer = 2.9 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "buc" = ( @@ -24149,7 +24115,7 @@ /area/station/maintenance/port) "bwz" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bwA" = ( @@ -24182,7 +24148,7 @@ /area/station/hallway/secondary/exit) "bwK" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bwL" = ( @@ -24884,7 +24850,7 @@ dir = 8 }, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "freezerfloor" }, @@ -25420,7 +25386,7 @@ /area/space) "bCj" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/port) "bCk" = ( @@ -26047,7 +26013,7 @@ /obj/structure/rack, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "bER" = ( @@ -26094,7 +26060,7 @@ pixel_x = 4; pixel_y = 3 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bFi" = ( @@ -28003,7 +27969,7 @@ icon_state = "4-8" }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bMg" = ( @@ -31121,7 +31087,7 @@ /area/station/command/office/rd) "bYk" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint2) "bYm" = ( @@ -32426,7 +32392,7 @@ d2 = 2; icon_state = "1-2" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/cyan, /turf/simulated/floor/plating, @@ -33591,7 +33557,7 @@ /area/station/medical/cryo) "cho" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "chp" = ( @@ -34066,7 +34032,7 @@ /area/station/maintenance/port) "cjf" = ( /obj/structure/table/glass, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -34802,7 +34768,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/securearea{ pixel_x = -32 }, @@ -34970,7 +34936,7 @@ /area/station/science/storage) "cmH" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "cmJ" = ( @@ -36435,7 +36401,7 @@ /area/station/science/toxins/mixing) "csw" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "csB" = ( @@ -36478,7 +36444,7 @@ /area/station/science/server/coldroom) "csK" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "csL" = ( @@ -39003,7 +38969,7 @@ /turf/simulated/floor/plasteel, /area/station/engineering/controlroom) "cAU" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint2) "cAW" = ( @@ -39641,7 +39607,7 @@ /area/station/maintenance/aft) "cDo" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "cDs" = ( @@ -40358,7 +40324,7 @@ dir = 8; layer = 2.9 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -41733,7 +41699,7 @@ /area/station/maintenance/asmaint) "cKF" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "cKK" = ( @@ -41749,12 +41715,12 @@ }, /area/station/maintenance/aft) "cKO" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "cKQ" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint2) "cKR" = ( @@ -41828,7 +41794,7 @@ /area/station/science/test_chamber) "cLc" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint2) "cLd" = ( @@ -42168,7 +42134,7 @@ /obj/machinery/atmospherics/pipe/simple/visible{ dir = 9 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cMt" = ( @@ -43226,7 +43192,7 @@ "cQs" = ( /obj/item/wrench, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint2) "cQv" = ( @@ -43249,11 +43215,6 @@ icon_state = "purple" }, /area/station/science/hallway) -"cQC" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/asmaint) "cQE" = ( /turf/simulated/floor/plasteel, /area/station/command/office/ce) @@ -43608,7 +43569,7 @@ d2 = 8; icon_state = "2-8" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/purple, /obj/structure/cable{ d1 = 2; @@ -43639,7 +43600,7 @@ /area/station/maintenance/port) "cRE" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -44279,7 +44240,7 @@ /area/station/engineering/control) "cTB" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "cTD" = ( @@ -46250,7 +46211,7 @@ /area/station/maintenance/aft) "dbg" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint2) "dbi" = ( @@ -46272,7 +46233,7 @@ /obj/structure/closet/crate/freezer, /obj/item/organ/internal/heart/vox, /obj/item/organ/internal/liver/vox, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/organ/internal/heart/vox, /turf/simulated/floor/plasteel{ icon_state = "freezerfloor" @@ -46280,7 +46241,7 @@ /area/station/maintenance/asmaint) "dbq" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/cobweb2, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -46334,7 +46295,7 @@ "dbD" = ( /obj/structure/table, /obj/item/extinguisher, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/cyan{ dir = 4 }, @@ -47150,7 +47111,7 @@ dir = 10 }, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "der" = ( @@ -47411,11 +47372,6 @@ }, /turf/simulated/floor/plating, /area/station/maintenance/solar_maintenance/aft_starboard) -"dfc" = ( -/obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/asmaint2) "dfg" = ( /obj/machinery/photocopier, /turf/simulated/floor/wood, @@ -47828,13 +47784,13 @@ /area/station/engineering/atmos) "dgX" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/roller, /turf/simulated/floor/plating, /area/station/maintenance/aft) "dgY" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/food/donkpocket, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -47867,7 +47823,7 @@ /area/station/turret_protected/aisat/interior) "dhk" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/item/toy/minimeteor, /obj/item/poster/random_contraband, /turf/simulated/floor/plating, @@ -49333,14 +49289,9 @@ /area/station/maintenance/aft) "dmq" = ( /obj/structure/disposalpipe/segment, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) -"dmr" = ( -/obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/aft) "dms" = ( /obj/structure/cable{ d1 = 1; @@ -50659,9 +50610,6 @@ name = "west bump"; pixel_x = -28 }, -/obj/machinery/requests_console/directional/south{ - pixel_x = 30 - }, /turf/simulated/floor/bluegrid, /area/station/turret_protected/ai) "drV" = ( @@ -51113,7 +51061,7 @@ /area/station/public/arcade) "dwT" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) @@ -51143,7 +51091,7 @@ /area/station/medical/morgue) "dxN" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "dxQ" = ( @@ -52321,7 +52269,7 @@ pixel_x = 2; pixel_y = 2 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "eaU" = ( @@ -52741,7 +52689,7 @@ /obj/machinery/atmospherics/pipe/simple/visible{ dir = 5 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) "ehp" = ( @@ -53316,7 +53264,7 @@ /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint2) "euQ" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/closet, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -53782,7 +53730,7 @@ dir = 4 }, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_frequent, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -53888,7 +53836,7 @@ /area/station/security/prison/cell_block/A) "eIN" = ( /obj/structure/table/reinforced, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/eftpos/register, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -54043,12 +53991,12 @@ }, /area/station/engineering/smes) "eNn" = ( -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "eNs" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/fpmaint) "eND" = ( @@ -54088,19 +54036,13 @@ /area/station/public/dorms) "eOI" = ( /obj/structure/closet/crate/trashcart, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/effect/decal/cleanable/dirt, /obj/item/wirerod, /obj/item/wirecutters, /obj/item/crowbar/large, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "eOZ" = ( @@ -54308,7 +54250,7 @@ "eUk" = ( /obj/structure/closet/crate/freezer, /obj/effect/spawner/lootdrop/three_course_meal, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal) "eUq" = ( @@ -55235,7 +55177,7 @@ "fof" = ( /obj/structure/rack, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "fog" = ( @@ -55340,7 +55282,7 @@ "fqt" = ( /obj/item/storage/bag/plasticbag, /obj/item/trash/fried_vox, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "fqw" = ( @@ -55874,7 +55816,7 @@ /area/station/maintenance/asmaint2) "fDd" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "fDx" = ( @@ -56045,14 +55987,6 @@ }, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) -"fHs" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plasteel{ - icon_state = "showroomfloor" - }, -/area/station/maintenance/fpmaint) "fHB" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/cyan, /obj/structure/cable{ @@ -56311,7 +56245,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "fPq" = ( -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/official/random{ pixel_y = 32 }, @@ -56582,7 +56516,7 @@ /area/station/hallway/primary/port/west) "fVy" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/item/storage/fancy/cigarettes/dromedaryco, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -56633,7 +56567,6 @@ /obj/machinery/light{ dir = 1 }, -/obj/item/storage/box/monkeycubes, /turf/simulated/floor/plasteel, /area/station/science/genetics) "fXp" = ( @@ -57390,7 +57323,7 @@ "gsd" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -57497,7 +57430,7 @@ /area/station/turret_protected/ai_upload) "gsY" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 8 }, @@ -57506,7 +57439,7 @@ /area/station/maintenance/aft) "gta" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "gto" = ( @@ -57561,7 +57494,7 @@ /obj/item/reagent_containers/condiment/peppermill{ pixel_x = -7 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/wood, /area/station/maintenance/aft) "guY" = ( @@ -57864,7 +57797,7 @@ /area/station/command/office/captain) "gEj" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/contraband/random{ pixel_x = -32 }, @@ -58929,7 +58862,7 @@ pixel_y = 30 }, /obj/structure/table_frame, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -59824,7 +59757,7 @@ /turf/simulated/floor/grass, /area/station/security/permabrig) "hAG" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment, /turf/simulated/floor/plating, /area/station/maintenance/asmaint2) @@ -59838,7 +59771,7 @@ /area/station/maintenance/aft) "hBu" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/robotanalyzer, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -60745,7 +60678,7 @@ /area/station/engineering/control) "hYf" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -60778,7 +60711,7 @@ "iac" = ( /obj/structure/table, /obj/item/wrench, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "iag" = ( @@ -60945,7 +60878,7 @@ /area/station/maintenance/asmaint) "ieW" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "ifp" = ( @@ -62894,7 +62827,7 @@ }, /area/station/medical/medbay2) "jgm" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/table, /turf/simulated/floor/plating/airless, /area/station/science/toxins/test) @@ -63102,7 +63035,7 @@ "jmU" = ( /obj/effect/decal/remains/human, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "jnc" = ( @@ -63120,7 +63053,7 @@ }, /obj/item/tank/internals/emergency_oxygen, /obj/item/clothing/mask/breath, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "jnh" = ( @@ -63635,7 +63568,7 @@ /area/station/maintenance/port) "jxj" = ( /obj/structure/table/reinforced, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -64273,7 +64206,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "jPx" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -64536,7 +64469,7 @@ /area/station/science/toxins/test) "jUM" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint2) "jUZ" = ( @@ -64731,7 +64664,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/cyan{ dir = 6 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/alarm/directional/west, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -65010,7 +64943,7 @@ /obj/structure/chair/sofa/right{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "khU" = ( @@ -65611,7 +65544,7 @@ /area/station/maintenance/disposal) "kwO" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "kwP" = ( @@ -66092,7 +66025,7 @@ /area/station/public/dorms) "kIa" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "kIP" = ( @@ -66510,7 +66443,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/cable{ d1 = 2; d2 = 4; @@ -67275,7 +67208,7 @@ }, /area/station/security/permabrig) "llx" = ( -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -67718,7 +67651,7 @@ }, /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random_spawners/cobweb_left_frequent, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/item/coin/silver{ pixel_x = 5; pixel_y = -8 @@ -68211,7 +68144,7 @@ /obj/effect/decal/cleanable/blood/xeno, /obj/structure/closet/crate/freezer, /obj/item/organ/internal/lungs/vox, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "freezerfloor" }, @@ -68432,24 +68365,11 @@ /turf/simulated/floor/plating, /area/station/maintenance/disposal) "lNE" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/storage/box/monkeycubes/wolpincubes, -/obj/item/storage/box/monkeycubes/nian_worme_cubes, /obj/machinery/light, -/obj/item/storage/box/monkeycubes/farwacubes{ - pixel_x = -6 - }, -/obj/item/storage/box/monkeycubes/neaeracubes, -/obj/item/storage/box/monkeycubes/stokcubes{ - pixel_x = 4; - pixel_y = -4 - }, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 }, +/obj/structure/closet/secure_closet/genetics, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, @@ -69153,7 +69073,7 @@ /area/station/medical/cryo) "meB" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/item/tank/internals/oxygen/yellow, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -69707,7 +69627,7 @@ "mtx" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/asmaint) "muh" = ( @@ -69915,7 +69835,7 @@ /area/station/security/permabrig) "mAq" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/cable{ d1 = 4; d2 = 8; @@ -69937,7 +69857,7 @@ /area/station/command/office/captain) "mBE" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 8 }, @@ -70073,7 +69993,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/purple{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "mEQ" = ( @@ -71420,7 +71340,7 @@ "nsu" = ( /obj/structure/closet/wardrobe/pjs, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "freezerfloor" }, @@ -71430,7 +71350,7 @@ /turf/simulated/wall/r_wall, /area/station/medical/virology) "nsP" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/closet/firecloset, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -71662,11 +71582,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) -"nyh" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/eight, -/turf/simulated/floor/plating, -/area/station/maintenance/aft) "nyC" = ( /obj/structure/rack, /turf/simulated/floor/plating, @@ -72651,7 +72566,7 @@ /area/station/science/test_chamber) "nXK" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "nYg" = ( @@ -73601,7 +73516,7 @@ /area/station/science/server/coldroom) "orE" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/blood_maybe, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -73856,7 +73771,7 @@ /turf/simulated/floor/plating/airless, /area/station/maintenance/asmaint) "oxe" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/airless, /area/station/maintenance/fpmaint2) "oxf" = ( @@ -73873,7 +73788,7 @@ /area/station/science/hallway) "oxy" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/engineering{ pixel_x = 29 }, @@ -74939,7 +74854,7 @@ /area/station/maintenance/fsmaint) "oVs" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_frequent, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint2) @@ -75170,7 +75085,7 @@ "paW" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table_frame, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/official/healthy{ pixel_x = 31 }, @@ -75612,7 +75527,7 @@ "pon" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "pop" = ( @@ -75864,7 +75779,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -75968,7 +75883,7 @@ /turf/simulated/floor/engine/co2, /area/station/engineering/atmos) "pxz" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/grille_often, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -76968,7 +76883,7 @@ /obj/effect/decal/cleanable/dirt, /obj/structure/rack, /obj/item/storage/box/bodybags, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -77467,7 +77382,7 @@ /area/station/security/permabrig) "qjC" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) @@ -78164,7 +78079,7 @@ /area/station/maintenance/apmaint) "qwM" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_frequent, /obj/machinery/light/small{ dir = 1 @@ -78284,7 +78199,7 @@ /area/station/hallway/secondary/exit) "qAg" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "cmo" }, @@ -78595,7 +78510,7 @@ /area/station/science/toxins/test) "qES" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel/airless, /area/station/science/toxins/test) "qFg" = ( @@ -79438,7 +79353,7 @@ /area/station/maintenance/asmaint) "raL" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/soap, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -79988,7 +79903,7 @@ /area/station/maintenance/aft) "rmI" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/cable{ d1 = 4; d2 = 8; @@ -80014,7 +79929,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "rnd" = ( @@ -80171,11 +80086,6 @@ icon_state = "freezerfloor" }, /area/station/public/toilet/unisex) -"rrg" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/apmaint) "rrh" = ( /obj/machinery/light/small, /obj/structure/disposalpipe/segment/corner{ @@ -80223,7 +80133,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_frequent, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -81275,7 +81185,7 @@ /area/station/hallway/primary/port/east) "rVK" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "rVV" = ( @@ -81376,7 +81286,7 @@ /area/station/science/rnd) "rYt" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "rYY" = ( @@ -81650,7 +81560,7 @@ /area/station/hallway/primary/port/east) "sfc" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/door/window/classic/normal{ dir = 1; name = "Kitchen" @@ -81904,7 +81814,7 @@ req_one_access = list(33,41); req_one_access_txt = "33;41" }, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /mob/living/simple_animal/hostile/scarybat, /turf/simulated/floor/plasteel{ icon_state = "vault" @@ -81982,7 +81892,7 @@ "spr" = ( /obj/structure/table, /obj/machinery/atmospherics/pipe/simple/hidden/cyan, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "spy" = ( @@ -82138,16 +82048,6 @@ icon_state = "bar" }, /area/station/security/permabrig) -"ssr" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/turf/simulated/floor/plating, -/area/station/maintenance/apmaint2) "ssF" = ( /obj/structure/closet/secure_closet/hop, /obj/machinery/camera/autoname{ @@ -82161,7 +82061,7 @@ }, /area/station/public/toilet/unisex) "ssL" = ( -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/gloves/color/blue, /turf/simulated/floor/plasteel{ icon_state = "freezerfloor" @@ -82469,7 +82369,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "sDp" = ( @@ -82765,7 +82665,7 @@ }, /area/station/command/office/ce) "sKQ" = ( -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/official/help_others{ pixel_y = 31 }, @@ -83215,13 +83115,6 @@ icon_state = "grimy" }, /area/station/command/office/hop) -"sXJ" = ( -/obj/structure/rack{ - dir = 1 - }, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/apmaint) "sXR" = ( /obj/structure/cable{ d1 = 1; @@ -83326,17 +83219,14 @@ dir = 1 }, /obj/item/stack/spacecash/c10, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/effect/decal/cleanable/dirt, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plasteel, /area/station/maintenance/port) "sZg" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "sZh" = ( @@ -83741,11 +83631,6 @@ icon_state = "dark" }, /area/station/engineering/control) -"tkT" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, -/turf/simulated/floor/plasteel, -/area/station/maintenance/apmaint) "tkW" = ( /obj/machinery/light/small{ dir = 1 @@ -83987,7 +83872,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "tue" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/damageturf, /turf/simulated/floor/wood, /area/station/maintenance/asmaint) @@ -84567,7 +84452,7 @@ /area/station/science/toxins/mixing) "tHO" = ( /obj/structure/chair/sofa/bench/right, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) @@ -85144,7 +85029,7 @@ /obj/item/crowbar, /obj/effect/landmark/spawner/nukedisc_respawn, /obj/effect/spawner/random_spawners/cobweb_left_frequent, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/wood, /area/station/maintenance/apmaint) "tUH" = ( @@ -85177,10 +85062,6 @@ }, /turf/simulated/floor/plasteel, /area/station/maintenance/aft) -"tVF" = ( -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/asmaint) "tVK" = ( /obj/structure/cable{ d2 = 4; @@ -85462,7 +85343,7 @@ /obj/structure/bed, /obj/effect/decal/cleanable/dirt, /obj/structure/closet/body_bag, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -85519,7 +85400,7 @@ /area/station/command/bridge) "ucE" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "ucL" = ( @@ -85602,7 +85483,7 @@ /area/station/maintenance/fsmaint) "ueT" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/storage/toolbox/mechanical, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -85630,7 +85511,7 @@ "ufC" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "ugC" = ( @@ -85748,7 +85629,7 @@ /area/station/maintenance/asmaint2) "ukQ" = ( /obj/structure/closet/body_bag, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "ukV" = ( @@ -86028,7 +85909,7 @@ }, /area/station/engineering/smes) "urU" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment/corner{ dir = 2 }, @@ -86556,7 +86437,7 @@ dir = 1 }, /obj/item/storage/toolbox/mechanical, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -87459,7 +87340,7 @@ /area/station/supply/sorting) "vic" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -88123,7 +88004,7 @@ /area/station/security/permabrig) "vxz" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/item/reagent_containers/syringe/insulin, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/syringe, @@ -88266,7 +88147,7 @@ "vBC" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "vBG" = ( @@ -88757,7 +88638,7 @@ /area/station/science/lobby) "vOy" = ( /obj/effect/spawner/random_spawners/blood_maybe, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -89113,7 +88994,7 @@ }, /area/station/science/genetics) "vZV" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/table, /obj/item/cartridge/signal/toxins, /turf/simulated/floor/plating, @@ -89467,7 +89348,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment/corner{ dir = 2 }, @@ -89574,7 +89455,7 @@ /area/station/security/permabrig) "wkQ" = ( /obj/effect/spawner/random_spawners/blood_maybe, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/cyan{ dir = 4 }, @@ -90006,7 +89887,7 @@ "wvD" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/burnturf, /obj/effect/landmark/spawner/nukedisc_respawn, /turf/simulated/floor/plating, @@ -90170,6 +90051,10 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) +"wBd" = ( +/obj/machinery/requests_console/directional/north, +/turf/simulated/floor/bluegrid, +/area/station/turret_protected/ai) "wBy" = ( /obj/machinery/light{ dir = 4 @@ -90204,7 +90089,7 @@ "wCk" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitegreen" @@ -90615,7 +90500,7 @@ /area/station/maintenance/asmaint) "wLu" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/restroom{ pixel_y = 31 }, @@ -90811,7 +90696,7 @@ /area/station/aisat/service) "wQI" = ( /obj/structure/closet/crate/internals, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "wQK" = ( @@ -90968,7 +90853,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "wXg" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/cyan{ dir = 4 }, @@ -91003,7 +90888,7 @@ }, /area/station/medical/chemistry) "wYs" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/contraband/random{ pixel_y = 32 }, @@ -91381,7 +91266,7 @@ /obj/effect/decal/cleanable/dirt, /obj/structure/rack, /obj/item/clothing/gloves/color/latex, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "xig" = ( @@ -91600,7 +91485,7 @@ /area/station/maintenance/assembly_line) "xmq" = ( /obj/structure/closet/crate/internals, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "xmr" = ( @@ -92229,7 +92114,7 @@ /turf/simulated/floor/plasteel, /area/station/hallway/primary/central/se) "xBC" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/engineering{ pixel_x = -27 }, @@ -93582,7 +93467,7 @@ /area/station/maintenance/aft) "ygC" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "ygM" = ( @@ -93674,7 +93559,7 @@ /area/station/engineering/hardsuitstorage) "yhY" = ( /obj/structure/table/reinforced, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/damageturf, /turf/simulated/floor/wood, /area/station/maintenance/apmaint2) @@ -93737,11 +93622,6 @@ }, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) -"yjF" = ( -/obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/asmaint) "yjH" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -106399,7 +106279,7 @@ aaa aaa aaa loS -aNA +aLB aKN aGn oan @@ -108194,7 +108074,7 @@ aED aFR aHg aIw -aNF +aKa aGn aLu jwb @@ -108451,7 +108331,7 @@ uQE aFT aHi aRA -aNF +aKa aGn fRo aLj @@ -108510,7 +108390,7 @@ coL cgQ gta gta -rrg +gta cgQ bCf bOd @@ -108754,7 +108634,7 @@ bET lRv aaa gzN -tkT +csw cwI cqK qjC @@ -110309,7 +110189,7 @@ cgQ yjC coL tGO -sXJ +sCW hoJ jVv rqF @@ -111797,8 +111677,8 @@ blG aHl aLu aHS -aNA -aNA +aLB +aLB aGn aKb aHl @@ -115137,7 +115017,7 @@ aMA aLF aMT aOo -fHs +aPu aMA wnG hNI @@ -116416,7 +116296,7 @@ axb axb aMA aGj -aHs +aGb aIT aJO aOr @@ -117784,7 +117664,7 @@ bKD cLi cLi tEU -ssr +mAq kkk rne bUv @@ -119052,7 +118932,7 @@ cwl wFR fnV cwz -sXJ +sCW sCW cgQ dCM @@ -124386,7 +124266,7 @@ aGy ayV ayA axe -aMj +aCz sZa pVr mSN @@ -131337,7 +131217,7 @@ bbw bdm bfz aOI -aMo +aMi bbI bdT bdM @@ -131458,7 +131338,7 @@ drf drf drf drf -dry +wBd drO drI dry @@ -134420,7 +134300,7 @@ mxZ knB aMz aLc -aNh +aHb aQL mvU ttT @@ -135024,7 +134904,7 @@ chf rmI cep cYj -dmr +csK klf aaa aaa @@ -135696,7 +135576,7 @@ aGT aGT aGX aHb -aNh +aHb aOG aGX aHb @@ -136032,7 +135912,7 @@ rAi xRP chf ebS -nyh +cTB jFf chf cyJ @@ -136216,7 +136096,7 @@ aMz aOF aOG aGX -aUb +aLc aGY aGX aWb @@ -136981,7 +136861,7 @@ aGX aGY aQI aGX -aMo +aMi aRJ aTe xRy @@ -137043,8 +136923,8 @@ dHU sQt cQk lbZ -dfc -dfc +bYk +bYk bGG bGG xBC @@ -138066,7 +137946,7 @@ dcK bGG cuS qmw -dfc +bYk dcW cpH bGG @@ -138522,8 +138402,8 @@ aGY aGY aGY aKV -aMo -aMo +aMi +aMi aGY aMz aQO @@ -140156,7 +140036,7 @@ ioE rHJ yfJ oJr -cQC +cKF dhR csL ciY @@ -140935,7 +140815,7 @@ ciY ciY ciY ciY -tVF +eNn csL ciY ciY @@ -141188,7 +141068,7 @@ kMc kdc csL djP -cQC +cKF fof dlz dSu @@ -142211,7 +142091,7 @@ uXA uXA wUT ciY -cQC +cKF csL qdO ycI @@ -142468,7 +142348,7 @@ ciY ciY oUq ciY -cQC +cKF csL dhR ycI @@ -144258,7 +144138,7 @@ ciY dHc pOY ciY -yjF +cDo ePu ugC ciY @@ -144483,7 +144363,7 @@ oUq ciY ciY cKF -tVF +eNn csL ciY hdW @@ -144510,7 +144390,7 @@ iDg cga fGT nBI -tVF +eNn ciY ylP ciY @@ -144733,7 +144613,7 @@ gww csL rng csL -tVF +eNn ciY fVy dbX @@ -144748,7 +144628,7 @@ qQj fUW cgs cgs -tVF +eNn csL uzh rSS @@ -145249,7 +145129,7 @@ mZs oUq csL dbX -tVF +eNn ciY ciY cgs @@ -145803,7 +145683,7 @@ csL ciY dbi csL -tVF +eNn ciY ciY faP @@ -147084,7 +146964,7 @@ cpE cQw ciY ePu -tVF +eNn ciY ciY vMe @@ -147849,7 +147729,7 @@ cga ciY pbc ctq -tVF +eNn kpR raH eba @@ -148101,7 +147981,7 @@ ece kBI eNn ciY -tVF +eNn iZV ciY mQX @@ -148361,7 +148241,7 @@ ciY euQ ePu jgA -tVF +eNn gmU jPx gmU @@ -148881,7 +148761,7 @@ ciY ciY ciY ciY -cQC +cKF meB ciY aab diff --git a/_maps/map_files/stations/cerestation.dmm b/_maps/map_files/stations/cerestation.dmm index d159c958bb5f..79ee60ed51b7 100644 --- a/_maps/map_files/stations/cerestation.dmm +++ b/_maps/map_files/stations/cerestation.dmm @@ -240,7 +240,7 @@ /area/station/maintenance/disposal/external/north) "abH" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/starboard) "abJ" = ( @@ -435,7 +435,7 @@ /area/station/turret_protected/ai) "acK" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/stripes/line{ dir = 4 }, @@ -1925,7 +1925,7 @@ dir = 1 }, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/north) "alW" = ( @@ -2108,7 +2108,7 @@ dir = 8 }, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/north) "ang" = ( @@ -2328,7 +2328,7 @@ /area/station/security/permabrig) "aoE" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/northeast) "aoF" = ( @@ -2507,7 +2507,7 @@ /area/station/turret_protected/aisat/interior) "apu" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -2689,9 +2689,9 @@ /area/station/security/prisonlockers) "arc" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fpmaint) "ard" = ( @@ -2806,7 +2806,7 @@ /area/station/maintenance/disposal/northeast) "arQ" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/structure/cable/orange{ d1 = 4; d2 = 8; @@ -3009,8 +3009,8 @@ /area/station/security/prisonlockers) "atB" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fore2) "atC" = ( @@ -3409,7 +3409,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "avS" = ( @@ -3422,7 +3422,7 @@ }, /area/station/maintenance/fore2) "awc" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fore2) "awd" = ( @@ -4233,7 +4233,7 @@ /area/station/science/rnd) "aCb" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/cobweb, /turf/simulated/floor/plating, /area/station/maintenance/fore2) @@ -5907,7 +5907,7 @@ /area/station/telecomms/computer) "aPc" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "aPd" = ( @@ -6210,7 +6210,7 @@ /area/station/security/permabrig) "aQR" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/maintcentral) "aQS" = ( @@ -6676,7 +6676,7 @@ /area/station/public/arcade) "aUi" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/camera{ c_tag = "Fore Asteroid Maintenance APCs 2"; dir = 5 @@ -6828,7 +6828,7 @@ /area/station/maintenance/disposal/north) "aUU" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -7432,7 +7432,7 @@ /area/station/turret_protected/aisat/interior) "aYC" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port2) "aYD" = ( @@ -7916,7 +7916,7 @@ /area/station/hallway/primary/central/north) "bbv" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/central/north) "bbw" = ( @@ -7926,7 +7926,7 @@ /area/station/maintenance/disposal/south) "bbC" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/port/north) "bbH" = ( @@ -8011,7 +8011,7 @@ "bcf" = ( /obj/structure/closet/crate, /obj/item/pickaxe/mini, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "bck" = ( @@ -10670,7 +10670,7 @@ dir = 4 }, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fore) "bqR" = ( @@ -11086,7 +11086,7 @@ /area/station/medical/medbay) "bsA" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fpmaint) "bsB" = ( @@ -12630,7 +12630,7 @@ /area/station/security/processing) "bzh" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fpmaint) "bzj" = ( @@ -12640,7 +12640,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/east) "bzq" = ( @@ -15747,7 +15747,7 @@ /area/station/command/bridge) "bMX" = ( /obj/structure/closet/wardrobe/mixed, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bMY" = ( @@ -15927,13 +15927,8 @@ }, /turf/simulated/floor/plating, /area/station/hallway/primary/starboard/south) -"bNL" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating/asteroid/ancient, -/area/station/maintenance/starboard) "bNM" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fore) "bNT" = ( @@ -16038,7 +16033,7 @@ dir = 4 }, /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -18148,7 +18143,7 @@ }, /obj/effect/decal/cleanable/cobweb, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "bWl" = ( @@ -18219,7 +18214,7 @@ /area/station/maintenance/port) "bWG" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "bWJ" = ( @@ -18438,7 +18433,7 @@ "bXu" = ( /obj/structure/rack, /obj/item/pickaxe/emergency, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "bXv" = ( @@ -18521,7 +18516,7 @@ dir = 1 }, /obj/effect/spawner/random_spawners/dirt_frequent, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/item/paper/crumpled, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -18759,11 +18754,11 @@ /area/station/maintenance/gambling_den) "bZt" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/gambling_den) "bZv" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/closet/crate, /turf/simulated/floor/plating, /area/station/maintenance/storage) @@ -19074,7 +19069,7 @@ /area/station/maintenance/fore2) "ccV" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fore2) "ccW" = ( @@ -19210,7 +19205,7 @@ /area/station/security/prisonershuttle) "cdO" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "cdP" = ( @@ -19507,8 +19502,8 @@ /area/station/public/quantum/docking) "cfH" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/asmaint) "cfN" = ( @@ -20307,11 +20302,8 @@ "ckQ" = ( /obj/machinery/navbeacon{ codes_txt = "patrol;next_patrol=ArrivalsMiddle"; - location = "ArrivalsWest2"; - name = "navigation beacon (Arrivals-West2)" - }, -/obj/structure/disposalpipe/segment{ - dir = 4 + location = "ArrivalsWest"; + name = "navigation beacon (Arrivals-West)" }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -20789,7 +20781,7 @@ /obj/structure/closet/emcloset, /obj/machinery/light/small, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "coO" = ( @@ -20825,7 +20817,7 @@ /obj/machinery/light/small{ dir = 8 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "coX" = ( @@ -20882,7 +20874,7 @@ "cpo" = ( /obj/structure/table, /obj/effect/spawner/random_spawners/dirt_frequent, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -20992,7 +20984,7 @@ /area/station/maintenance/port) "cpQ" = ( /obj/structure/closet/firecloset/full, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -21190,7 +21182,7 @@ /area/station/hallway/secondary/exit) "crC" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ dir = 10; @@ -21401,7 +21393,7 @@ "ctC" = ( /obj/structure/rack, /obj/item/pickaxe, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -21790,7 +21782,7 @@ /area/station/security/prison/cell_block/A) "cxd" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/securearea{ desc = "A warning sign which reads 'EXTERNAL AIRLOCK'"; icon_state = "space"; @@ -21861,8 +21853,8 @@ /area/station/science/toxins/mixing) "cxU" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -22089,7 +22081,7 @@ "czP" = ( /obj/structure/closet/crate, /obj/item/pickaxe, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/apmaint) "czV" = ( @@ -22142,9 +22134,7 @@ }, /area/station/science/xenobiology) "cAn" = ( -/obj/machinery/requests_console{ - pixel_y = 30 - }, +/obj/machinery/requests_console/directional/north, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "darkbrown" @@ -22690,7 +22680,7 @@ icon_state = "1-4" }, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "cDW" = ( @@ -23889,7 +23879,7 @@ /area/shuttle/arrival/station) "cJN" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/central/west) "cJO" = ( @@ -24286,8 +24276,8 @@ /area/station/maintenance/disposal/external/southeast) "cMw" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "cMx" = ( @@ -25326,7 +25316,7 @@ /area/station/command/bridge) "cQU" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/storage) "cQW" = ( @@ -25341,7 +25331,7 @@ /area/station/maintenance/storage) "cRa" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/maintcentral) "cRh" = ( @@ -25470,7 +25460,7 @@ /area/station/hallway/primary/aft/west) "cRZ" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/pickaxe, /turf/simulated/floor/plating, /area/station/maintenance/storage) @@ -25526,12 +25516,12 @@ /obj/machinery/light/small{ dir = 8 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/fore/north) "cSj" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/fore/north) "cSk" = ( @@ -28746,7 +28736,7 @@ /area/station/maintenance/disposal/west) "doo" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fpmaint) "dos" = ( @@ -29397,7 +29387,7 @@ dir = 1 }, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "dsc" = ( @@ -31423,7 +31413,7 @@ /area/station/hallway/primary/fore/west) "dCU" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/starboard) "dCV" = ( @@ -32626,15 +32616,16 @@ /turf/simulated/floor/plating, /area/station/maintenance/starboard) "dTC" = ( +/obj/machinery/hologram/holopad, /obj/machinery/navbeacon{ - codes_txt = "patrol;next_patrol=Security"; - location = "CommandMiddle2"; - name = "navigation beacon (Command-Middle 2)" + codes_txt = "patrol;next_patrol=CargoWest"; + location = "MedbayWest"; + name = "navigation beacon (Medbay-West)" }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, -/area/station/hallway/primary/fore/north) +/area/station/hallway/primary/starboard/south) "dTI" = ( /obj/machinery/navbeacon{ codes_txt = "patrol;next_patrol=EngineeringMiddle"; @@ -33222,7 +33213,7 @@ /area/station/medical/morgue) "eeN" = ( /obj/effect/spawner/random_spawners/blood_maybe, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -33230,7 +33221,7 @@ "eeZ" = ( /obj/structure/closet/crate, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "efa" = ( @@ -33820,7 +33811,7 @@ dir = 1 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/window/basic{ dir = 8 }, @@ -33828,7 +33819,7 @@ /area/station/maintenance/disposal) "eot" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/maintcentral) "eoz" = ( @@ -34046,7 +34037,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/storage) "etG" = ( @@ -34127,7 +34118,7 @@ /area/station/science/xenobiology) "eux" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/maintcentral) "euD" = ( @@ -34519,7 +34510,7 @@ /obj/structure/closet/crate{ name = "top secret mime supplies" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -35493,7 +35484,7 @@ /turf/simulated/floor/carpet/green, /area/station/service/library) "eOu" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -35747,7 +35738,7 @@ /area/station/hallway/spacebridge/scidock) "eTI" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "eTJ" = ( @@ -36235,19 +36226,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) -"fbA" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, -/obj/structure/cable{ - d1 = 1; - d2 = 8; - icon_state = "1-8" - }, -/turf/simulated/floor/plating{ - icon_state = "asteroidplating" - }, -/area/station/maintenance/asmaint) "fcm" = ( /obj/structure/chair/office/dark{ dir = 1 @@ -36289,7 +36267,7 @@ /obj/structure/disposalpipe/segment/corner{ dir = 8 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -36644,17 +36622,6 @@ icon_state = "browncorner" }, /area/station/supply/lobby) -"fka" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/structure/disposalpipe/segment/corner, -/obj/structure/cable{ - d1 = 2; - d2 = 8; - icon_state = "2-8" - }, -/turf/simulated/floor/plating, -/area/station/maintenance/asmaint) "fkc" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/computer/area_atmos{ @@ -37775,7 +37742,7 @@ icon_state = "4-8" }, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "fFz" = ( @@ -38820,7 +38787,7 @@ /area/station/hallway/primary/fore/west) "fVg" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "fVo" = ( @@ -39166,6 +39133,17 @@ icon_state = "redyellowfull" }, /area/station/service/bar) +"gah" = ( +/obj/effect/spawner/random/maintenance, +/obj/structure/closet/crate, +/obj/structure/disposalpipe/segment/corner, +/obj/structure/cable{ + d1 = 2; + d2 = 8; + icon_state = "2-8" + }, +/turf/simulated/floor/plating, +/area/station/maintenance/asmaint) "gaj" = ( /obj/machinery/atmospherics/pipe/simple/visible{ dir = 10 @@ -39436,7 +39414,7 @@ /area/station/security/range) "gge" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/asmaint) "ggt" = ( @@ -39862,7 +39840,7 @@ /area/station/maintenance/disposal/west) "goE" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/maintcentral) @@ -40352,7 +40330,7 @@ }, /area/station/hallway/primary/port/south) "gxp" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/service/clown/secret) @@ -41162,7 +41140,7 @@ /area/station/maintenance/fsmaint) "gLj" = ( /obj/structure/table/wood, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -41830,7 +41808,7 @@ /area/station/service/bar) "gWd" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/maintcentral) "gXb" = ( @@ -42240,7 +42218,7 @@ "hcK" = ( /obj/effect/decal/cleanable/cobweb, /obj/structure/closet/wardrobe/black, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "hcL" = ( @@ -42248,7 +42226,7 @@ /obj/effect/decal/cleanable/cobweb, /obj/effect/decal/cleanable/dirt, /obj/item/storage/fancy/cigarettes/cigpack_random, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitepurple" @@ -42381,7 +42359,7 @@ /area/station/command/office/ntrep) "heH" = ( /obj/machinery/navbeacon{ - codes_txt = "patrol;next_patrol=Medbay"; + codes_txt = "patrol;next_patrol=MedbayWest"; location = "ArrivalsMiddle"; name = "navigation beacon (Arrivals-Middle)" }, @@ -42814,7 +42792,7 @@ /area/station/maintenance/storage) "hmo" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "hmp" = ( @@ -43288,7 +43266,7 @@ /area/station/hallway/primary/fore/east) "huF" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/west) "huM" = ( @@ -43413,7 +43391,7 @@ /area/station/maintenance/disposal/west) "hxc" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -43462,7 +43440,7 @@ /area/station/maintenance/storage) "hxO" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "hxR" = ( @@ -43551,7 +43529,7 @@ /area/station/security/lobby) "hzj" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment/corner{ dir = 4 }, @@ -43663,7 +43641,7 @@ }, /obj/effect/decal/cleanable/cobweb, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/north) "hAX" = ( @@ -44038,7 +44016,7 @@ /area/station/science/hallway) "hHb" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/gambling_den) "hHu" = ( @@ -44151,7 +44129,7 @@ icon_state = "1-2" }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "hIo" = ( @@ -44583,7 +44561,7 @@ /area/station/public/quantum/security) "hPG" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/roller, /turf/simulated/floor/plating, /area/station/maintenance/starboard) @@ -44919,7 +44897,7 @@ "hSU" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/fore/north) "hSY" = ( @@ -45467,7 +45445,7 @@ /area/station/command/office/ntrep) "ibf" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/maintcentral) "ibJ" = ( @@ -46256,8 +46234,8 @@ pixel_x = 1; pixel_y = 5 }, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/west) "imU" = ( @@ -46315,7 +46293,7 @@ /area/station/hallway/primary/fore/east) "inP" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -47531,11 +47509,6 @@ icon_state = "neutralcorner" }, /area/station/hallway/primary/fore/east) -"iEW" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/starboard) "iEY" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 @@ -47761,7 +47734,7 @@ /obj/machinery/light/small{ dir = 8 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/fore/north) "iIW" = ( @@ -47864,14 +47837,14 @@ /area/station/hallway/primary/port/north) "iJJ" = ( /obj/machinery/navbeacon{ - codes_txt = "patrol;next_patrol=ArrivalsWest2"; - location = "ArrivalsWest"; - name = "navigation beacon (Arrivals-West)" + codes_txt = "patrol;next_patrol=CommandMiddle"; + location = "CargoWest"; + name = "navigation beacon (Cargo-West)" }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, -/area/station/hallway/secondary/entry/west) +/area/station/hallway/primary/fore/east) "iJM" = ( /obj/effect/spawner/random_spawners/dirt_frequent, /obj/effect/spawner/random_spawners/grille_maybe, @@ -48032,7 +48005,7 @@ /area/station/science/toxins/launch) "iNd" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "iNj" = ( @@ -48175,7 +48148,7 @@ /area/station/maintenance/fore) "iOU" = ( /obj/structure/table/wood, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/wood, /area/station/maintenance/starboard) "iPa" = ( @@ -48882,13 +48855,13 @@ "iYY" = ( /obj/structure/closet, /obj/effect/decal/cleanable/cobweb, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/mine/unexplored/cere/orbiting) "iZd" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/fore/west) "iZr" = ( @@ -49160,7 +49133,7 @@ /area/station/engineering/atmos) "jbp" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/flashlight, /turf/simulated/floor/transparent/glass/reinforced, /area/station/maintenance/starboard) @@ -50029,7 +50002,7 @@ /obj/structure/disposalpipe/segment/corner{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/east) "jpP" = ( @@ -50750,7 +50723,7 @@ /area/station/command/office/rd) "jyk" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "jyx" = ( @@ -50954,7 +50927,7 @@ /turf/space, /area/station/hallway/spacebridge/cargocom) "jAx" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/table, /obj/effect/landmark/burnturf, /obj/item/robotanalyzer, @@ -51335,7 +51308,7 @@ /area/station/maintenance/port) "jFd" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/fore/north) "jFg" = ( @@ -51836,10 +51809,7 @@ /area/station/maintenance/port) "jMh" = ( /obj/item/kirbyplants, -/obj/machinery/requests_console{ - department = "Psychiatrist"; - pixel_x = -30 - }, +/obj/machinery/requests_console/directional/west, /turf/simulated/floor/carpet, /area/station/medical/psych) "jMr" = ( @@ -52360,7 +52330,7 @@ /area/station/hallway/secondary/entry/east) "jUd" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -52383,7 +52353,7 @@ pixel_x = 5; pixel_y = -2 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/starboard) "jUz" = ( @@ -52529,7 +52499,7 @@ /area/station/hallway/primary/starboard/south) "jXm" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "jXt" = ( @@ -53170,7 +53140,7 @@ pixel_y = -3; pixel_x = 3 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "darkpurple" @@ -53245,8 +53215,8 @@ dir = 8 }, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/storage) "kjT" = ( @@ -53872,7 +53842,7 @@ icon_state = "1-2" }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "kuj" = ( @@ -54015,7 +53985,7 @@ /area/station/command/office/ntrep) "kwQ" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment/corner{ dir = 4 }, @@ -54169,7 +54139,7 @@ /area/station/hallway/secondary/entry/south) "kyo" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/storage) "kyp" = ( @@ -54660,7 +54630,7 @@ "kIn" = ( /obj/structure/table, /obj/item/storage/fancy/cigarettes, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/south) "kIo" = ( @@ -55874,7 +55844,7 @@ "kZw" = ( /obj/structure/table, /obj/item/storage/fancy/cigarettes, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/sortjunction/reversed{ dir = 4; sort_type_txt = "25" @@ -56618,11 +56588,6 @@ icon_state = "neutralfull" }, /area/station/hallway/primary/central/east) -"ljR" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/starboard) "ljV" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 @@ -56693,7 +56658,7 @@ /area/station/supply/expedition) "lkL" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "llc" = ( @@ -58387,7 +58352,7 @@ /area/station/maintenance/apmaint) "lJM" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/external/southeast) "lJN" = ( @@ -60111,7 +60076,7 @@ "mkA" = ( /obj/item/storage/toolbox/mechanical, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/south) "mkS" = ( @@ -60149,7 +60114,7 @@ dir = 4 }, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/east) "mlM" = ( @@ -60188,7 +60153,7 @@ /area/station/maintenance/fsmaint) "mmx" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "mmz" = ( @@ -60339,7 +60304,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/disposal/external/southwest) "moQ" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/fore2) @@ -61213,7 +61178,7 @@ /area/station/security/main) "mCE" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/pickaxe, /turf/simulated/floor/plating, /area/station/maintenance/storage) @@ -61443,7 +61408,7 @@ "mGY" = ( /obj/item/storage/box/bodybags, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "mHg" = ( @@ -61597,8 +61562,8 @@ /area/station/hallway/primary/port/north) "mJn" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -61922,7 +61887,7 @@ /area/station/maintenance/electrical_shop) "mPu" = ( /obj/structure/table/wood/poker, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/barsign{ pixel_y = 32 }, @@ -62304,8 +62269,8 @@ /area/station/hallway/secondary/entry/north) "mUe" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "mUu" = ( @@ -63194,7 +63159,7 @@ /area/station/hallway/secondary/exit) "njs" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/pickaxe, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) @@ -63218,7 +63183,7 @@ /area/station/hallway/spacebridge/engmed) "njx" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/maintcentral) "njB" = ( @@ -63778,7 +63743,7 @@ /area/station/turret_protected/ai_upload) "ntf" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "nto" = ( @@ -63982,7 +63947,7 @@ /area/station/engineering/control) "nvA" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -64174,7 +64139,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "nyX" = ( -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -64659,7 +64624,7 @@ /area/station/security/detective) "nHK" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/port/north) "nHR" = ( @@ -65426,7 +65391,7 @@ path_to_spawn = /mob/living/simple_animal/hostile/scarybat; total_amount = 20 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -65788,7 +65753,7 @@ /turf/simulated/floor/transparent/glass/reinforced, /area/station/maintenance/port) "oci" = ( -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/hallway/spacebridge/scidock) "ocw" = ( @@ -66586,14 +66551,10 @@ }, /area/station/service/bar) "onW" = ( -/obj/machinery/requests_console{ - department = "Janitorial"; - departmentType = 1; - pixel_y = 30 - }, /obj/effect/turf_decal/loading_area{ dir = 4 }, +/obj/machinery/requests_console/directional/north, /turf/simulated/floor/plasteel, /area/station/service/janitor) "ooj" = ( @@ -67770,7 +67731,7 @@ pixel_x = -13; pixel_y = 2 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -67854,7 +67815,7 @@ "oJH" = ( /obj/structure/closet/crate, /obj/item/pickaxe/emergency, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/apmaint) "oJY" = ( @@ -70380,12 +70341,6 @@ icon_state = "whitepurplecorner" }, /area/station/science/misc_lab) -"pvo" = ( -/obj/effect/spawner/lootdrop/maintenance/three, -/turf/simulated/floor/plating{ - icon_state = "asteroidplating" - }, -/area/station/hallway/spacebridge/scidock) "pvH" = ( /obj/structure/chair/sofa/pew/right{ dir = 8 @@ -70610,7 +70565,7 @@ "pzK" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "pzS" = ( @@ -71153,7 +71108,7 @@ /turf/simulated/floor/wood, /area/station/service/bar) "pIc" = ( -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/closet, /turf/simulated/floor/plating, /area/station/maintenance/starboard) @@ -71245,7 +71200,7 @@ "pKj" = ( /obj/structure/table, /obj/machinery/light, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 }, @@ -71663,7 +71618,7 @@ /area/station/supply/storage) "pPI" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "pPM" = ( @@ -72212,7 +72167,7 @@ /area/station/maintenance/apmaint) "pWH" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/starboard) "pWQ" = ( @@ -72251,7 +72206,7 @@ /area/station/service/theatre) "pXp" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/food/donkpocket, /turf/simulated/floor/plating, /area/station/maintenance/starboard) @@ -72681,7 +72636,7 @@ dir = 8 }, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "qcS" = ( @@ -72869,7 +72824,7 @@ /area/station/service/library) "qfv" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -73066,6 +73021,18 @@ icon_state = "dark" }, /area/station/service/chapel) +"qhZ" = ( +/obj/effect/spawner/random/maintenance, +/obj/structure/closet/crate, +/obj/structure/cable{ + d1 = 1; + d2 = 8; + icon_state = "1-8" + }, +/turf/simulated/floor/plating{ + icon_state = "asteroidplating" + }, +/area/station/maintenance/asmaint) "qii" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -73483,7 +73450,7 @@ /area/station/hallway/secondary/exit) "qov" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/pickaxe/emergency, /turf/simulated/floor/plating, /area/station/maintenance/starboard) @@ -73593,7 +73560,7 @@ /area/station/maintenance/apmaint) "qpM" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/maintcentral) @@ -75460,7 +75427,7 @@ pixel_x = 1 }, /obj/item/storage/toolbox/mechanical, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/aft/west) "qUu" = ( @@ -77395,7 +77362,7 @@ /area/station/maintenance/disposal/external/southwest) "rFq" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/hallway/primary/aft/west) "rFJ" = ( @@ -77891,7 +77858,7 @@ /area/station/command/bridge) "rMP" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/southwest) "rMT" = ( @@ -79260,8 +79227,8 @@ "shB" = ( /obj/structure/rack, /obj/item/pickaxe/emergency, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -79849,7 +79816,7 @@ "srv" = ( /obj/structure/table, /obj/machinery/cell_charger, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/transparent/glass/reinforced, /area/station/maintenance/starboard) "srN" = ( @@ -80377,16 +80344,6 @@ icon_state = "darkblue" }, /area/station/command/office/cmo) -"swR" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/structure/cable/orange{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/turf/simulated/floor/plating, -/area/station/maintenance/starboard) "sxa" = ( /obj/effect/spawner/random_spawners/wall_rusted_always, /turf/simulated/wall, @@ -82130,7 +82087,7 @@ /area/station/security/warden) "sWZ" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/southwest) "sXj" = ( @@ -82348,11 +82305,6 @@ icon_state = "white" }, /area/station/medical/virology) -"taQ" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating/asteroid/ancient, -/area/station/maintenance/port) "taW" = ( /obj/machinery/light/small{ dir = 1 @@ -82487,7 +82439,7 @@ /area/station/public/vacant_office) "tcw" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/gambling_den) "tcx" = ( @@ -82754,9 +82706,9 @@ /area/station/maintenance/disposal/external/southwest) "thz" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment/corner{ dir = 1 }, @@ -83177,7 +83129,7 @@ /area/station/hallway/spacebridge/scidock) "tnj" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/apmaint) "tnA" = ( @@ -83434,8 +83386,8 @@ /area/station/supply/sorting) "tsc" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/maintcentral) "tsf" = ( @@ -83806,8 +83758,8 @@ /area/station/engineering/tech_storage) "twx" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/fpmaint) "twF" = ( @@ -84382,7 +84334,7 @@ /area/station/hallway/spacebridge/dockmed) "tFK" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -85392,8 +85344,8 @@ /area/station/maintenance/starboard) "tXA" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "tXE" = ( @@ -88196,11 +88148,6 @@ "uMr" = ( /turf/simulated/wall/r_wall, /area/station/science/rnd) -"uMu" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/maintcentral) "uMC" = ( /obj/machinery/door/airlock/public/glass{ name = "Rehabilitation Dome" @@ -88475,7 +88422,7 @@ "uQh" = ( /obj/effect/decal/cleanable/cobweb, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment/corner, /turf/simulated/floor/plating{ icon_state = "asteroidplating" @@ -88944,14 +88891,8 @@ /turf/simulated/floor/plating, /area/station/hallway/primary/starboard/south) "uVC" = ( -/obj/structure/closet/secure_closet/personal/patient, -/obj/item/storage/box/monkeycubes, -/obj/item/storage/box/monkeycubes/farwacubes, -/obj/item/storage/box/monkeycubes/neaeracubes, -/obj/item/storage/box/monkeycubes/stokcubes, -/obj/item/storage/box/monkeycubes/wolpincubes, -/obj/item/storage/box/monkeycubes/nian_worme_cubes, /obj/machinery/requests_console/directional/west, +/obj/structure/closet/secure_closet/genetics, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitepurple" @@ -90029,11 +89970,6 @@ icon_state = "tranquillite" }, /area/station/service/mime) -"vok" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/maintcentral) "vos" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -90098,7 +90034,7 @@ pixel_y = 5 }, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitepurple" @@ -90527,8 +90463,8 @@ /area/station/medical/cloning) "vun" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -90891,7 +90827,7 @@ /obj/item/coin/silver, /obj/item/coin/silver, /obj/item/pickaxe, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "vzj" = ( @@ -91871,11 +91807,6 @@ icon_state = "darkred" }, /area/station/security/checkpoint/secondary) -"vOv" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating/asteroid/ancient, -/area/station/maintenance/starboard) "vOI" = ( /turf/simulated/floor/carpet/cyan, /area/station/public/fitness) @@ -91956,6 +91887,19 @@ /obj/structure/reagent_dispensers/fueltank, /turf/simulated/floor/plating, /area/station/maintenance/electrical_shop) +"vQu" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/navbeacon{ + codes_txt = "patrol;next_patrol=Security"; + location = "CommandMiddle2"; + name = "navigation beacon (Command-Middle 2)" + }, +/turf/simulated/floor/plasteel{ + icon_state = "neutralfull" + }, +/area/station/hallway/primary/fore/north) "vQv" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 @@ -94156,7 +94100,7 @@ /area/station/hallway/primary/starboard/south) "wuY" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/public/storefront) @@ -95378,7 +95322,7 @@ /area/station/maintenance/fsmaint) "wJY" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -96303,7 +96247,7 @@ /area/station/maintenance/gambling_den) "wZl" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/plating, /area/station/hallway/primary/central/north) @@ -96495,7 +96439,7 @@ /area/station/maintenance/port) "xaU" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "xbh" = ( @@ -96603,7 +96547,7 @@ /obj/structure/rack, /obj/item/storage/toolbox/mechanical, /obj/item/weldingtool/hugetank, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal/east) "xcH" = ( @@ -96960,7 +96904,7 @@ d2 = 2; icon_state = "1-2" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -96971,7 +96915,7 @@ "xiV" = ( /obj/structure/closet/emcloset, /obj/effect/decal/cleanable/cobweb, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/port) "xja" = ( @@ -97533,7 +97477,7 @@ /area/station/legal/courtroom) "xtR" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/asteroid/ancient, /area/station/maintenance/gambling_den) "xtS" = ( @@ -98586,7 +98530,7 @@ /area/station/engineering/control) "xGC" = ( /obj/structure/closet/crate/internals, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/dirt_frequent, /turf/simulated/floor/plasteel{ dir = 5; @@ -98667,7 +98611,7 @@ pixel_x = 3; pixel_y = 3 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/electrical_shop) "xHO" = ( @@ -98762,7 +98706,7 @@ /area/station/public/quantum/science) "xIU" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating{ icon_state = "asteroidplating" }, @@ -99859,7 +99803,7 @@ /area/station/maintenance/port) "xZC" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/airlock_electronics, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -100158,7 +100102,7 @@ dir = 1 }, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "yeW" = ( @@ -114771,7 +114715,7 @@ rzu mHH ijL ccx -taQ +mmx vtY vtY usD @@ -132979,7 +132923,7 @@ bAK mZB bEi bUO -ugQ +vQu sGY npV abW @@ -135107,7 +135051,7 @@ mkk fLT fLT dkk -uMu +aQR cUr rgm rgm @@ -135383,7 +135327,7 @@ cYC cYM wzj cYC -pvo +nyX cYC cYC jGj @@ -136320,7 +136264,7 @@ abW abW aKp dwg -dTC +vuY qtB aKp npV @@ -139192,7 +139136,7 @@ oLP iuC bvj fLT -uMu +aQR nVW nVW nVW @@ -140540,7 +140484,7 @@ mIS vLT vaz eSb -dbg +ckQ eWu rFJ qHn @@ -140754,7 +140698,7 @@ wJC vUQ wJC mkk -vok +eot hxE aXR aXR @@ -141052,10 +140996,10 @@ gge vWF vWF uDW -iJJ +dbg sFj dbg -ckQ +eWu vjb qHn rNK @@ -141561,7 +141505,7 @@ mpB mpB ixC bKg -fbA +qhZ vWF vWF vWF @@ -144644,7 +144588,7 @@ aXn aXn aXn wLr -fka +gah pXf yep xSo @@ -145869,7 +145813,7 @@ kxJ bvt was bvt -ljp +dTC xcW qcS qcS @@ -148930,7 +148874,7 @@ fmI fmI fmI fmI -fmI +iJJ fmI fmI fmI @@ -154368,7 +154312,7 @@ gbJ gbJ bLk cFA -bNL +abH tvJ xjx vbn @@ -156163,7 +156107,7 @@ uyn bkH biN wyL -iEW +fVg itf qIV xAV @@ -157182,7 +157126,7 @@ oXg bil gFg gSQ -vOv +pWH biN eEd bkH @@ -157942,7 +157886,7 @@ alc gSQ xAV qWh -swR +arQ bil dzF dzP @@ -159986,7 +159930,7 @@ rNK rNK lCC alc -ljR +jXm yhg umz gSQ diff --git a/_maps/map_files/stations/deltastation.dmm b/_maps/map_files/stations/deltastation.dmm index 90ba1744be31..444e36e09a4f 100644 --- a/_maps/map_files/stations/deltastation.dmm +++ b/_maps/map_files/stations/deltastation.dmm @@ -1703,7 +1703,7 @@ /area/station/maintenance/fore2) "amV" = ( /obj/structure/table/wood, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "redyellowfull" }, @@ -1851,7 +1851,7 @@ /area/station/maintenance/fore2) "anC" = ( /obj/structure/table/reinforced, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery, /turf/simulated/floor/plasteel, /area/station/maintenance/fore2) @@ -2799,7 +2799,7 @@ /area/station/maintenance/fore2) "aqm" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -2807,7 +2807,7 @@ "aqn" = ( /obj/structure/rack, /obj/item/crowbar, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ icon_state = "neutral" @@ -2816,7 +2816,7 @@ "aqo" = ( /obj/structure/closet/crate, /obj/item/flashlight, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, /area/station/maintenance/fore2) @@ -3996,7 +3996,7 @@ name = "booze cabinet" }, /obj/effect/decal/cleanable/cobweb, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/damageturf, /turf/simulated/floor/wood, /area/station/maintenance/fore) @@ -4569,7 +4569,7 @@ /obj/machinery/light/small{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "auQ" = ( @@ -4700,7 +4700,7 @@ /obj/machinery/light/small{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore2) "avl" = ( @@ -5859,12 +5859,12 @@ /area/station/maintenance/fore) "axZ" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aya" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "neutral" @@ -6158,7 +6158,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 9 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/disposal) "azh" = ( @@ -6553,7 +6553,7 @@ "aAk" = ( /obj/structure/rack, /obj/item/reagent_containers/drinks/bottle/whiskey, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aAl" = ( @@ -6775,7 +6775,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery/hollow, /obj/structure/disposalpipe/segment{ dir = 4 @@ -6789,7 +6789,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery, /obj/structure/disposalpipe/segment{ dir = 4 @@ -7066,7 +7066,7 @@ /area/station/supply/storage) "aBo" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aBp" = ( @@ -7081,7 +7081,7 @@ /obj/structure/table/wood, /obj/item/clothing/shoes/jackboots, /obj/effect/landmark/costume/random, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "cafeteria" }, @@ -7702,7 +7702,7 @@ /area/station/supply/storage) "aDj" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery, /turf/simulated/floor/plasteel, /area/station/supply/sorting) @@ -7924,7 +7924,7 @@ /area/station/supply/storage) "aDN" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/supply/sorting) "aDO" = ( @@ -8290,7 +8290,7 @@ }, /area/station/supply/storage) "aEX" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery/hollow, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -8298,7 +8298,7 @@ /area/station/supply/storage) "aEY" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery/hollow, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -8541,7 +8541,7 @@ "aFG" = ( /obj/structure/closet/crate, /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery/hollow, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -9242,7 +9242,7 @@ /area/station/maintenance/abandoned_garden) "aHz" = ( /obj/structure/closet/cardboard, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery/hollow, /turf/simulated/floor/plasteel{ @@ -10389,7 +10389,7 @@ /obj/machinery/conveyor{ id = "cargodisposals" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/supply/sorting) "aKB" = ( @@ -10437,7 +10437,7 @@ /area/station/supply/storage) "aKL" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/simulated/floor/plasteel{ @@ -10758,7 +10758,7 @@ /area/station/engineering/controlroom) "aLx" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -10815,7 +10815,7 @@ /area/station/service/barber) "aLH" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "neutral" @@ -11385,7 +11385,7 @@ }, /area/station/supply/storage) "aNw" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -12411,7 +12411,7 @@ /turf/simulated/floor/wood, /area/station/maintenance/gambling_den) "aQr" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery/hollow, /turf/simulated/floor/plasteel{ @@ -12866,7 +12866,7 @@ /area/station/service/bar) "aRu" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/damageturf, /turf/simulated/floor/plasteel/airless{ dir = 10; @@ -18563,7 +18563,7 @@ /area/station/maintenance/fore) "bgo" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/fore) "bgp" = ( @@ -19707,7 +19707,7 @@ /obj/machinery/light/small{ dir = 8 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/fore) "bjd" = ( @@ -19880,7 +19880,7 @@ /area/station/service/kitchen) "bjy" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "bjz" = ( @@ -21183,7 +21183,7 @@ "bmP" = ( /obj/structure/rack, /obj/effect/landmark/costume/random, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/fore) "bmQ" = ( @@ -23831,7 +23831,7 @@ pixel_y = -32 }, /obj/machinery/flasher{ - id = "Cell 5"; + id = "Cell 2"; pixel_x = -28 }, /obj/structure/cable, @@ -26841,7 +26841,7 @@ /obj/item/assembly/igniter, /obj/item/assembly/igniter, /obj/item/assembly/igniter, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery/hollow, /turf/simulated/floor/plasteel, /area/station/public/storage/tools) @@ -27348,9 +27348,6 @@ /obj/item/radio/intercom/custom{ pixel_y = 25 }, -/obj/machinery/requests_console/directional/north{ - pixel_x = 30 - }, /turf/simulated/floor/plasteel/dark, /area/station/turret_protected/ai) "bCY" = ( @@ -30757,7 +30754,7 @@ /obj/machinery/light{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "cautioncorner" @@ -30765,7 +30762,7 @@ /area/station/public/storage/tools/auxiliary) "bLm" = ( /obj/structure/closet/toolcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "yellow" @@ -31677,7 +31674,7 @@ dir = 1 }, /obj/machinery/flasher{ - id = "Cell 3"; + id = "Cell 4"; pixel_x = -26; pixel_y = -26 }, @@ -32205,7 +32202,7 @@ /obj/structure/rack, /obj/item/storage/toolbox/emergency, /obj/item/storage/toolbox/emergency, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "yellow" @@ -35679,7 +35676,7 @@ /area/station/legal/magistrate) "bYU" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/railing{ dir = 1 }, @@ -35977,7 +35974,7 @@ /area/station/maintenance/port) "bZI" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bZJ" = ( @@ -36742,7 +36739,7 @@ /area/station/engineering/smes) "cby" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "neutral" @@ -37555,7 +37552,7 @@ /area/station/engineering/smes) "cdr" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -38184,12 +38181,6 @@ /obj/machinery/power/apc/critical/directional/south, /turf/simulated/floor/plasteel/dark, /area/station/engineering/smes) -"cfk" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/decal/cleanable/dirt, -/turf/simulated/floor/plating, -/area/station/maintenance/port) "cfn" = ( /obj/effect/decal/cleanable/fungus, /turf/simulated/wall, @@ -39481,7 +39472,7 @@ /area/station/public/locker) "cjm" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "cjn" = ( @@ -39491,7 +39482,7 @@ "cjo" = ( /obj/structure/rack, /obj/item/book/manual/wiki/security_space_law, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "cjp" = ( @@ -40653,7 +40644,7 @@ /area/station/legal/lawoffice) "cmq" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/starboard2) "cmr" = ( @@ -41524,7 +41515,7 @@ "coU" = ( /obj/structure/closet, /obj/effect/decal/cleanable/cobweb, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "neutral" @@ -42781,7 +42772,7 @@ "csd" = ( /obj/structure/table, /obj/item/storage/box/bodybags, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "cse" = ( @@ -42793,7 +42784,7 @@ "csf" = ( /obj/structure/closet/crate, /obj/item/clothing/shoes/jackboots, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "csh" = ( @@ -45345,7 +45336,7 @@ }, /obj/item/clothing/gloves/color/black, /obj/item/wrench, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery/hollow, /turf/simulated/floor/plasteel, /area/station/maintenance/port) @@ -46007,7 +45998,7 @@ /area/station/maintenance/port) "cBd" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "neutral" @@ -46972,7 +46963,7 @@ /area/station/maintenance/port) "cEa" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ dir = 4; @@ -47263,7 +47254,7 @@ "cFk" = ( /obj/structure/table, /obj/effect/decal/cleanable/cobweb2, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "cFl" = ( @@ -47540,7 +47531,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "neutral" @@ -48247,7 +48238,7 @@ /area/station/maintenance/port) "cHo" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -48255,7 +48246,7 @@ "cHp" = ( /obj/structure/rack, /obj/item/clothing/gloves/color/fyellow, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -48820,7 +48811,7 @@ /area/station/engineering/equipmentstorage) "cIS" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "cIT" = ( @@ -49151,13 +49142,13 @@ /area/station/maintenance/port) "cKf" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/port) "cKg" = ( /obj/structure/rack, /obj/item/book/manual/wiki/engineering_guide, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/port) "cKi" = ( @@ -49812,11 +49803,6 @@ icon_state = "neutralcorner" }, /area/station/public/fitness) -"cMS" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, -/turf/simulated/floor/plating, -/area/station/maintenance/port) "cMU" = ( /obj/machinery/atmospherics/binary/valve, /obj/effect/turf_decal/stripes/line{ @@ -50727,7 +50713,7 @@ /obj/structure/closet, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/structure/cable{ d1 = 1; d2 = 2; @@ -51602,7 +51588,7 @@ /area/station/maintenance/starboard) "cTc" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "neutral" @@ -51937,7 +51923,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "cUA" = ( @@ -52904,7 +52890,7 @@ /area/station/maintenance/port) "cXY" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -53086,7 +53072,7 @@ "cZc" = ( /obj/structure/rack, /obj/item/clothing/accessory/stethoscope, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutral" @@ -54691,7 +54677,7 @@ /area/station/maintenance/starboard) "dey" = ( /obj/structure/table/wood, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/wood, /area/station/maintenance/starboard) "deA" = ( @@ -55364,7 +55350,7 @@ /area/station/public/construction) "dhq" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutral" @@ -55757,7 +55743,7 @@ }, /obj/item/crowbar/red, /obj/item/wrench, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "yellowcorner" @@ -57100,7 +57086,7 @@ "dot" = ( /obj/effect/decal/cleanable/cobweb2, /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "dou" = ( @@ -57841,15 +57827,11 @@ }, /area/station/science/genetics) "drR" = ( -/obj/structure/table/glass, -/obj/item/storage/box/monkeycubes{ - pixel_x = -3 - }, -/obj/item/reagent_containers/spray/cleaner, /obj/machinery/light{ dir = 4 }, /obj/structure/disposalpipe/segment, +/obj/structure/closet/secure_closet/genetics, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurple" @@ -58394,7 +58376,7 @@ /area/station/maintenance/starboard) "duK" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -59133,7 +59115,7 @@ /area/station/maintenance/library) "dzo" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/costume/random, /obj/effect/landmark/costume/random, /turf/simulated/floor/plasteel{ @@ -60267,7 +60249,7 @@ /area/station/science/toxins/test) "dES" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -60916,11 +60898,6 @@ }, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) -"dIA" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/apmaint) "dID" = ( /turf/simulated/floor/plasteel{ icon_state = "neutral" @@ -61206,7 +61183,7 @@ "dKH" = ( /obj/structure/closet/crate, /obj/item/flashlight, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "dKI" = ( @@ -62692,7 +62669,7 @@ /area/station/maintenance/apmaint) "dRL" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "dRM" = ( @@ -62814,7 +62791,7 @@ /area/station/service/chapel) "dSn" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery, /turf/simulated/floor/plasteel{ dir = 8; @@ -64056,6 +64033,7 @@ pixel_y = 10 }, /obj/structure/disposalpipe/segment, +/obj/item/reagent_containers/spray/cleaner, /turf/simulated/floor/plasteel{ icon_state = "whitepurplecorner" }, @@ -64562,7 +64540,7 @@ /area/station/maintenance/starboard2) "eeX" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/storage/box/bodybags, /turf/simulated/floor/plasteel{ icon_state = "neutral" @@ -67581,7 +67559,7 @@ /obj/machinery/fishtank/bowl, /obj/machinery/fishtank/bowl, /obj/machinery/fishtank/bowl, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/beach/sand, /area/station/maintenance/fsmaint) "fMc" = ( @@ -68162,7 +68140,7 @@ /area/station/aisat) "gbO" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small, /turf/simulated/floor/plasteel{ icon_state = "bar" @@ -68780,7 +68758,7 @@ /area/station/security/armory/secure) "gwu" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "gwZ" = ( @@ -69521,7 +69499,7 @@ /obj/structure/table/wood, /obj/item/folder/white, /obj/item/folder/red, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "grimy" }, @@ -70020,7 +69998,7 @@ "hjo" = ( /obj/structure/closet/crate, /obj/effect/landmark/costume/random, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "hjK" = ( @@ -70037,7 +70015,7 @@ name = "north bump"; pixel_y = 28 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel/dark, /area/station/maintenance/fore2) "hkM" = ( @@ -71805,7 +71783,7 @@ /area/station/public/pet_store) "iny" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "inH" = ( @@ -72257,7 +72235,7 @@ /area/station/medical/reception) "iBK" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "neutral" @@ -72689,7 +72667,7 @@ dir = 1 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "iQb" = ( @@ -73016,7 +72994,7 @@ /area/station/hallway/primary/central/nw) "iXL" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "iXM" = ( @@ -74053,7 +74031,7 @@ /area/station/engineering/atmos) "jJj" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "jJl" = ( @@ -75085,7 +75063,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/aft) "kkB" = ( -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitepurple" @@ -75144,7 +75122,7 @@ /area/station/hallway/primary/central/north) "klS" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "kmr" = ( @@ -76162,7 +76140,7 @@ "kSX" = ( /obj/structure/closet/crate, /obj/item/flashlight, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -77105,7 +77083,7 @@ dir = 4 }, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -77188,7 +77166,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 6 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "lxC" = ( @@ -79728,7 +79706,7 @@ /area/station/hallway/primary/central/east) "mWO" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/cobweb, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) @@ -79879,7 +79857,7 @@ /obj/structure/rack, /obj/item/storage/fancy/matches, /obj/item/storage/fancy/cigarettes/cigpack_robust, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "naB" = ( @@ -80723,7 +80701,7 @@ /area/station/maintenance/abandoned_garden) "nyK" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "nzB" = ( @@ -81533,6 +81511,10 @@ icon_state = "darkred" }, /area/station/security/brig) +"nYK" = ( +/obj/machinery/requests_console/directional/south, +/turf/simulated/floor/plasteel/dark, +/area/station/turret_protected/ai) "nYW" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 @@ -81883,7 +81865,7 @@ /area/station/turret_protected/aisat) "okf" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -82537,7 +82519,7 @@ /area/station/aisat) "oBE" = ( /obj/item/vending_refill/coffee, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "oBK" = ( @@ -83226,7 +83208,7 @@ /obj/structure/closet/crate{ opened = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "neutral" }, @@ -83831,7 +83813,7 @@ "pmA" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "pmI" = ( @@ -84949,7 +84931,7 @@ /obj/structure/rack, /obj/item/multitool, /obj/item/wrench, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/damageturf, /turf/simulated/floor/wood, /area/station/maintenance/apmaint) @@ -85087,7 +85069,7 @@ "pWt" = ( /obj/effect/spawner/random_spawners/cobweb_right_frequent, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "pWw" = ( @@ -85187,7 +85169,7 @@ /area/station/medical/cryo) "qaf" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/retractor, /turf/simulated/floor/plasteel{ dir = 8; @@ -86286,7 +86268,7 @@ /area/station/engineering/controlroom) "qOd" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -86796,7 +86778,7 @@ }, /area/station/hallway/primary/central/south) "qZI" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "qZV" = ( @@ -87190,11 +87172,6 @@ temperature = 80 }, /area/station/science/xenobiology) -"rkv" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/three, -/turf/simulated/floor/plating, -/area/station/maintenance/port) "rkA" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -87243,7 +87220,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 10 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "rly" = ( @@ -88064,7 +88041,7 @@ /obj/effect/decal/cleanable/cobweb2, /obj/effect/decal/cleanable/dirt, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "rJk" = ( @@ -88225,7 +88202,7 @@ "rOe" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutral" @@ -89844,7 +89821,7 @@ /area/station/maintenance/apmaint) "szf" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "szk" = ( @@ -90144,7 +90121,7 @@ /area/space/nearstation) "sHd" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "bar" }, @@ -90243,7 +90220,7 @@ "sJD" = ( /obj/structure/table, /obj/item/hand_labeler, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -90497,7 +90474,7 @@ "sQa" = ( /obj/structure/closet, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "sQr" = ( @@ -92151,7 +92128,7 @@ icon_state = "2-8" }, /obj/machinery/flasher{ - id = "Cell 6"; + id = "Cell 5"; pixel_x = 27; pixel_y = 28 }, @@ -92398,7 +92375,7 @@ "tVb" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutral" @@ -93004,7 +92981,7 @@ }, /area/station/public/fitness) "ujH" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ icon_state = "neutral" @@ -93352,7 +93329,7 @@ /area/station/maintenance/apmaint) "uud" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/accessory/stethoscope, /turf/simulated/floor/plating, /area/station/maintenance/aft) @@ -93374,7 +93351,7 @@ /area/station/maintenance/apmaint) "uvD" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ icon_state = "redyellowfull" @@ -94503,7 +94480,7 @@ /area/station/engineering/controlroom) "vhF" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "vhS" = ( @@ -96431,7 +96408,7 @@ /area/station/maintenance/starboard) "wnt" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/delivery/hollow, /obj/structure/cable{ d1 = 1; @@ -99297,7 +99274,7 @@ "xRD" = ( /obj/effect/spawner/random_spawners/cobweb_right_frequent, /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "xRM" = ( @@ -109468,7 +109445,7 @@ bBf bBf bIl bip -bBf +nYK bNM bPJ bRD @@ -119776,7 +119753,7 @@ cie cie cie bXU -rkv +jJj drn dhG cHA @@ -120553,7 +120530,7 @@ drn cIv dhG cLA -cMS +cIS cLA cQu cLA @@ -128753,7 +128730,7 @@ bYe cHA cby cdr -cfk +cdr drn cLA cQu @@ -134983,7 +134960,7 @@ jLf gNm iqc dHL -dIA +bjy dJx dKh dOr diff --git a/_maps/map_files/stations/metastation.dmm b/_maps/map_files/stations/metastation.dmm index a4ec0e7aaed7..9bc2a5c6c9ff 100644 --- a/_maps/map_files/stations/metastation.dmm +++ b/_maps/map_files/stations/metastation.dmm @@ -72,10 +72,7 @@ /turf/simulated/wall/r_wall, /area/station/security/permabrig) "abX" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fore) "abZ" = ( @@ -97,15 +94,12 @@ pixel_y = 2 }, /obj/item/taperecorder, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/structure/cable{ d1 = 4; d2 = 8; icon_state = "4-8" }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fore) "acv" = ( @@ -518,7 +512,7 @@ /obj/structure/table, /obj/item/multitool, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "agi" = ( @@ -574,7 +568,7 @@ /area/space) "agG" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "agH" = ( @@ -1221,7 +1215,7 @@ /area/station/maintenance/disposal) "alt" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/disposal) "alv" = ( @@ -1275,7 +1269,7 @@ /obj/item/clothing/suit/monkeysuit, /obj/item/clothing/head/xenos, /obj/item/clothing/mask/gas/monkeymask, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "alz" = ( @@ -1429,7 +1423,7 @@ /obj/item/clothing/gloves/color/yellow, /obj/item/mop, /obj/item/bikehorn/rubberducky, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "amv" = ( @@ -1568,7 +1562,7 @@ /area/station/engineering/gravitygenerator) "ano" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "anv" = ( @@ -1585,7 +1579,7 @@ /obj/structure/closet, /obj/item/poster/random_contraband, /obj/item/storage/box/lights/mixed, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "anA" = ( @@ -1668,7 +1662,7 @@ }, /obj/item/dice/d8, /obj/item/healthanalyzer, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "anK" = ( @@ -1871,10 +1865,7 @@ /obj/machinery/conveyor/north{ id = "garbage" }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/disposal) "aoU" = ( @@ -1936,7 +1927,7 @@ /obj/item/storage/secure/briefcase, /obj/item/disk/data, /obj/item/grenade/flashbang, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "api" = ( @@ -2190,21 +2181,21 @@ /obj/structure/closet/crate, /obj/item/bodybag, /obj/item/radio, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aqz" = ( /obj/structure/table/reinforced, /obj/structure/window/reinforced, /obj/item/stock_parts/cell/crap, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aqA" = ( /obj/structure/table/reinforced, /obj/structure/window/reinforced, /obj/item/firealarm_electronics, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aqB" = ( @@ -2220,7 +2211,7 @@ pixel_x = 4; pixel_y = -3 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/visible/cyan{ dir = 10 }, @@ -2300,7 +2291,7 @@ "arn" = ( /obj/structure/rack, /obj/item/extinguisher, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /turf/simulated/floor/plasteel, @@ -2633,7 +2624,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/fore) "ata" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating/airless, /area/space/nearstation) "atc" = ( @@ -2791,7 +2782,7 @@ /obj/machinery/light/small{ dir = 8 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "atJ" = ( @@ -3090,7 +3081,7 @@ /obj/structure/rack, /obj/item/storage/toolbox/emergency, /obj/effect/decal/cleanable/cobweb2, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "auB" = ( @@ -3115,7 +3106,7 @@ "auG" = ( /obj/structure/closet/crate, /obj/item/clothing/gloves/color/fyellow, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "auH" = ( @@ -3650,7 +3641,7 @@ /area/station/maintenance/fpmaint) "awM" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_frequent, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -3769,7 +3760,7 @@ /area/station/public/locker) "axb" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "axc" = ( @@ -3850,7 +3841,7 @@ /turf/simulated/floor/plating, /area/station/public/storage/emergency/port) "axt" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/damageturf, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) @@ -4362,7 +4353,7 @@ /area/station/maintenance/fsmaint) "azu" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "azy" = ( @@ -4493,10 +4484,7 @@ /area/station/maintenance/port) "azQ" = ( /obj/item/stack/sheet/cardboard, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/storage/box/mousetraps,/obj/item/storage/box/lights/tubes,/obj/item/storage/box/lights/mixed,/obj/item/storage/box/lights/bulbs); - name = "Janitor Supplies Spawner" - }, +/obj/effect/spawner/random/janitor/supplies, /turf/simulated/floor/plating, /area/station/maintenance/port2) "azR" = ( @@ -4575,7 +4563,7 @@ /obj/machinery/light/small{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "aAm" = ( @@ -4675,12 +4663,12 @@ /area/station/legal/courtroom/gallery) "aAO" = ( /obj/structure/closet/crate/freezer, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "aAP" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aAU" = ( @@ -4881,7 +4869,7 @@ /area/station/public/mrchangs) "aBT" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "aBV" = ( @@ -4922,7 +4910,7 @@ /turf/simulated/wall/r_wall, /area/station/maintenance/fsmaint) "aCa" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment{ dir = 4 }, @@ -5279,7 +5267,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aDD" = ( @@ -5650,10 +5638,7 @@ /obj/machinery/light/small{ dir = 1 }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/aft) "aEQ" = ( @@ -6012,16 +5997,13 @@ /area/station/maintenance/fore) "aGH" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/structure/cable{ d1 = 1; d2 = 2; icon_state = "1-2" }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fore) "aGJ" = ( @@ -6046,10 +6028,7 @@ /obj/machinery/conveyor/west{ id = "garbage" }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/disposal) "aGO" = ( @@ -6185,7 +6164,7 @@ "aHm" = ( /obj/structure/closet/crate, /obj/item/coin/silver, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aHn" = ( @@ -7566,7 +7545,7 @@ /area/station/service/bar) "aLD" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/public/construction) "aLE" = ( @@ -7753,11 +7732,8 @@ /turf/simulated/floor/plasteel, /area/station/supply/storage) "aMe" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aMf" = ( @@ -9148,7 +9124,7 @@ /area/station/command/office/hos) "aQQ" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 8 }, @@ -10230,7 +10206,7 @@ /turf/simulated/floor/plasteel, /area/station/hallway/primary/fore/east) "aUx" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/cyan, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 6 @@ -11245,7 +11221,7 @@ "aXr" = ( /obj/structure/table, /obj/item/storage/belt/utility, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, @@ -11476,7 +11452,7 @@ /area/station/hallway/secondary/entry/north) "aXT" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/public/construction) "aXU" = ( @@ -11537,7 +11513,7 @@ /area/station/hallway/secondary/entry/north) "aYb" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "aYh" = ( @@ -13190,7 +13166,7 @@ /area/station/security/permabrig) "bdl" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "bdm" = ( @@ -15453,10 +15429,7 @@ id = "packageSort2" }, /obj/effect/turf_decal/stripes/line, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/supply/office) "biG" = ( @@ -15519,7 +15492,7 @@ /area/station/hallway/primary/central/north) "biO" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "biS" = ( @@ -15727,7 +15700,7 @@ /obj/structure/rack, /obj/item/apc_electronics, /obj/item/airlock_electronics, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "yellow" }, @@ -16646,7 +16619,7 @@ /obj/item/clothing/gloves/color/fyellow, /obj/item/clothing/suit/storage/hazardvest, /obj/item/multitool, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light_switch{ dir = 4; name = "west bump"; @@ -16964,7 +16937,7 @@ /obj/structure/rack{ dir = 1 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "bmA" = ( @@ -19784,7 +19757,7 @@ /area/station/command/bridge) "buy" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 4 }, @@ -22400,7 +22373,7 @@ /obj/structure/closet, /obj/item/poster/random_contraband, /obj/effect/decal/cleanable/cobweb2, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bBO" = ( @@ -23690,11 +23663,8 @@ d2 = 4; icon_state = "0-4" }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/power/apc/directional/south, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/port) "bGd" = ( @@ -23886,7 +23856,7 @@ "bHp" = ( /obj/structure/rack, /obj/item/flashlight, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bHq" = ( @@ -24150,11 +24120,8 @@ /area/station/maintenance/starboard) "bHX" = ( /obj/machinery/atmospherics/pipe/simple/hidden, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/item/shard, +/obj/effect/spawner/random/trash, /turf/simulated/floor/bluegrid, /area/station/maintenance/starboard) "bHY" = ( @@ -26619,18 +26586,18 @@ /area/station/public/vacant_office) "bRD" = ( /obj/structure/table, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bRE" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bRF" = ( /obj/structure/closet, /obj/item/clothing/shoes/jackboots, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bRG" = ( @@ -27045,14 +27012,6 @@ icon_state = "dark" }, /area/station/medical/morgue) -"bTb" = ( -/obj/effect/landmark/damageturf, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/storage/box/mousetraps,/obj/item/storage/box/lights/tubes,/obj/item/storage/box/lights/mixed,/obj/item/storage/box/lights/bulbs); - name = "Janitor Supplies Spawner" - }, -/turf/simulated/floor/plating, -/area/station/maintenance/port) "bTd" = ( /obj/machinery/light_switch{ dir = 8; @@ -27171,7 +27130,7 @@ "bTr" = ( /obj/structure/closet/crate, /obj/item/rack_parts, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "bTs" = ( @@ -27463,7 +27422,7 @@ /area/station/public/vacant_office) "bUG" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden, /obj/machinery/light/small{ dir = 8 @@ -27899,10 +27858,7 @@ /turf/simulated/floor/carpet/blue, /area/station/command/office/blueshield) "bWi" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/port) "bWj" = ( @@ -28929,7 +28885,7 @@ dir = 4 }, /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -29042,11 +28998,9 @@ /area/station/maintenance/port) "caj" = ( /obj/structure/closet/crate/can, -/obj/effect/spawner/lootdrop/maintenance/two, -/obj/item/trash/pistachios, -/obj/item/trash/raisins, -/obj/item/trash/gum, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fore) "cam" = ( @@ -29381,7 +29335,7 @@ /area/station/service/hydroponics) "cbq" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/port) @@ -29929,7 +29883,7 @@ "cdm" = ( /obj/structure/rack, /obj/item/clothing/mask/gas, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "caution" @@ -30016,7 +29970,7 @@ /area/station/maintenance/port) "cdG" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "cdH" = ( @@ -30371,7 +30325,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "cfb" = ( @@ -31593,7 +31547,7 @@ /obj/item/stack/rods{ amount = 23 }, -/obj/effect/spawner/lootdrop/maintenance/eight, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cjq" = ( @@ -31990,7 +31944,7 @@ /area/station/engineering/solar/aft_port) "ckN" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/contraband/random{ pixel_y = 32 }, @@ -32077,7 +32031,7 @@ /obj/structure/sign/poster/random{ pixel_y = -32 }, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "greenblue" }, @@ -32113,7 +32067,7 @@ "clv" = ( /obj/item/food/monkeycube, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft2) "cly" = ( @@ -32171,7 +32125,7 @@ "clK" = ( /obj/structure/closet, /obj/item/extinguisher, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "clL" = ( @@ -32280,7 +32234,7 @@ "cmg" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/starboard) @@ -32722,12 +32676,6 @@ icon_state = "chapel" }, /area/station/service/chapel) -"coj" = ( -/obj/structure/closet, -/obj/item/extinguisher, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/apmaint) "cok" = ( /obj/structure/chair/wheelchair, /obj/item/radio/intercom{ @@ -33648,14 +33596,14 @@ "crQ" = ( /obj/effect/turf_decal/stripes/white/line, /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "crR" = ( /obj/structure/closet/crate, /obj/item/assembly/infra, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/stripes/white/line, /obj/machinery/atmospherics/unary/vent_pump/on, /turf/simulated/floor/plating, @@ -33814,7 +33762,7 @@ pixel_x = 7; pixel_y = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "csG" = ( @@ -33872,7 +33820,7 @@ amount = 34 }, /obj/item/extinguisher/mini, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "csT" = ( @@ -33940,16 +33888,13 @@ d2 = 8; icon_state = "4-8" }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cta" = ( @@ -34207,11 +34152,8 @@ d2 = 4; icon_state = "1-4" }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/storage/box/mousetraps,/obj/item/storage/box/lights/tubes,/obj/item/storage/box/lights/mixed,/obj/item/storage/box/lights/bulbs); - name = "Janitor Supplies Spawner" - }, /obj/effect/turf_decal/stripes/line, +/obj/effect/spawner/random/janitor/supplies, /turf/simulated/floor/plating, /area/station/public/storage/emergency/port) "cuc" = ( @@ -34857,7 +34799,7 @@ /area/station/maintenance/turbine) "cwC" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cwD" = ( @@ -34909,7 +34851,7 @@ /area/station/maintenance/aft) "cwQ" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_frequent, /turf/simulated/floor/plating, /area/station/maintenance/starboard) @@ -35090,7 +35032,7 @@ /obj/structure/rack, /obj/item/reagent_containers/glass/bucket, /obj/item/mop, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "cxE" = ( @@ -35251,7 +35193,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -35433,7 +35375,7 @@ /area/station/science/server/coldroom) "cyT" = ( /obj/item/toy/plushie/shark, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/effect/landmark/spawner/rev, /obj/effect/decal/cleanable/blood/old, /obj/structure/closet/crate, @@ -35447,7 +35389,7 @@ /area/station/engineering/atmos) "cze" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "czg" = ( @@ -35561,10 +35503,7 @@ }, /area/station/medical/storage) "czw" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/aft) "czx" = ( @@ -35724,7 +35663,7 @@ /area/station/medical/virology) "cAu" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "cAw" = ( @@ -36221,13 +36160,13 @@ /obj/machinery/light/small{ dir = 4 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cCp" = ( /obj/structure/closet/crate, /obj/effect/landmark/damageturf, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cCr" = ( @@ -36719,7 +36658,7 @@ /area/station/command/office/captain/bedroom) "cEh" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cEj" = ( @@ -36777,7 +36716,7 @@ }, /area/station/science/xenobiology) "cEr" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "cEu" = ( @@ -37005,7 +36944,7 @@ dir = 4 }, /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -37109,13 +37048,10 @@ }, /area/station/medical/virology) "cFG" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/bluegrid, /area/station/maintenance/starboard) "cFH" = ( @@ -37146,7 +37082,7 @@ "cFU" = ( /obj/structure/closet/crate, /obj/effect/turf_decal/delivery/hollow, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "cFY" = ( @@ -38056,15 +37992,12 @@ }, /area/station/medical/cloning) "cIS" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/structure/sink{ dir = 8; pixel_x = -12; pixel_y = 2 }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cIT" = ( @@ -38497,7 +38430,7 @@ /area/station/medical/surgery/observation) "cKB" = ( /obj/structure/closet/crate/plastic, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "cKC" = ( @@ -38721,7 +38654,7 @@ "cLr" = ( /obj/structure/closet/firecloset, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "caution" @@ -38810,7 +38743,7 @@ /area/station/service/chapel) "cLN" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "cLO" = ( @@ -39190,7 +39123,7 @@ /area/station/maintenance/solar_maintenance/aft_port) "cNw" = ( /obj/structure/closet/wardrobe/white, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/medmaint) "cNz" = ( @@ -39401,7 +39334,7 @@ /area/station/hallway/primary/aft/south) "cOh" = ( /obj/structure/closet/crate/freezer, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ dir = 5; @@ -39668,11 +39601,8 @@ /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cPq" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/storage/box/mousetraps,/obj/item/storage/box/lights/tubes,/obj/item/storage/box/lights/mixed,/obj/item/storage/box/lights/bulbs); - name = "Janitor Supplies Spawner" - }, /obj/effect/landmark/damageturf, +/obj/effect/spawner/random/janitor/supplies, /turf/simulated/floor/plating, /area/station/maintenance/port) "cPr" = ( @@ -39751,7 +39681,7 @@ pixel_x = 32 }, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cPL" = ( @@ -39849,11 +39779,6 @@ }, /turf/simulated/floor/plating/airless, /area/station/science/toxins/test) -"cQb" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/port) "cQc" = ( /obj/structure/window/reinforced{ dir = 1 @@ -39968,7 +39893,7 @@ /obj/item/stack/cable_coil, /obj/item/wrench, /obj/item/flashlight/seclite, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cQI" = ( @@ -40065,7 +39990,7 @@ /area/station/maintenance/solar_maintenance/aft_starboard) "cRc" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "cRe" = ( @@ -40449,12 +40374,6 @@ }, /turf/simulated/floor/wood, /area/station/command/office/ntrep) -"cSK" = ( -/obj/structure/closet/crate, -/obj/effect/turf_decal/delivery/hollow, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plasteel, -/area/station/maintenance/apmaint) "cSL" = ( /obj/structure/table/wood, /obj/item/clothing/under/misc/burial, @@ -40691,7 +40610,7 @@ "cTD" = ( /obj/effect/landmark/burnturf, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "cTH" = ( @@ -40966,7 +40885,7 @@ /area/station/service/chapel) "cUL" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft2) "cUM" = ( @@ -41355,7 +41274,7 @@ /area/station/hallway/secondary/exit) "cWF" = ( /obj/structure/closet/secure_closet/personal, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/suit/storage/labcoat/science, /obj/item/clothing/under/rank/rnd/scientist, /obj/effect/decal/cleanable/dirt, @@ -41524,7 +41443,7 @@ /area/station/maintenance/port2) "cXu" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -41747,7 +41666,7 @@ }, /area/station/medical/exam_room) "cYF" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "cYG" = ( @@ -41849,10 +41768,7 @@ /area/station/medical/surgery/primary) "cZi" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "cZl" = ( @@ -41934,7 +41850,7 @@ "cZD" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "cZI" = ( @@ -42085,7 +42001,7 @@ /area/station/maintenance/asmaint) "das" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) "dat" = ( @@ -42407,7 +42323,7 @@ /area/station/engineering/solar/fore_starboard) "dcj" = ( /obj/structure/chair/sofa/left, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) @@ -42646,7 +42562,7 @@ /area/space) "ddR" = ( /obj/structure/closet/secure_closet/personal, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/suit/storage/labcoat/science, /obj/item/clothing/under/rank/rnd/scientist, /obj/effect/decal/cleanable/dirt, @@ -42663,10 +42579,6 @@ }, /obj/item/restraints/handcuffs, /obj/item/radio/off, -/obj/machinery/requests_console/directional/east{ - department = "Security"; - departmentType = 5 - }, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "red" @@ -43077,10 +42989,7 @@ /area/station/hallway/secondary/entry/south) "dgg" = ( /obj/effect/landmark/burnturf, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/port) "dgj" = ( @@ -43196,7 +43105,7 @@ d2 = 8; icon_state = "4-8" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -43206,7 +43115,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "dhl" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/rack, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) @@ -44372,10 +44281,7 @@ /area/station/hallway/primary/central/se) "dCJ" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "dCQ" = ( @@ -44654,7 +44560,7 @@ /area/station/service/hydroponics) "dLg" = ( /obj/item/rack_parts, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "dLF" = ( @@ -45384,7 +45290,7 @@ }, /area/station/public/toilet/lockerroom) "ech" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/table, /obj/effect/spawner/random_spawners/cobweb_left_rare, /turf/simulated/floor/plating, @@ -45985,7 +45891,7 @@ "erF" = ( /obj/structure/closet/crate/radiation, /obj/effect/turf_decal/delivery, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "erM" = ( @@ -46311,7 +46217,7 @@ /area/station/engineering/atmos) "eyo" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "eyP" = ( @@ -47992,7 +47898,7 @@ /turf/space, /area/space/nearstation) "fiU" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 8 }, @@ -48191,7 +48097,7 @@ "fmm" = ( /obj/structure/table, /obj/item/clothing/glasses/meson, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "caution" @@ -48916,13 +48822,13 @@ }, /obj/item/book/manual/wiki/hacking, /obj/item/tape/random, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "fAs" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "fAH" = ( @@ -49759,7 +49665,7 @@ /area/station/science/xenobiology) "fRm" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_rare, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -49898,7 +49804,7 @@ /area/station/maintenance/starboard) "fUf" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "caution" @@ -50887,7 +50793,7 @@ /area/station/engineering/ai_transit_tube) "gqi" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 8 }, @@ -51104,7 +51010,7 @@ "gwG" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/cardboard, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/toy/plushie/lizardplushie, /turf/simulated/floor/plasteel{ icon_state = "white" @@ -51627,18 +51533,11 @@ /area/station/engineering/control) "gHN" = ( /obj/structure/closet/crate/trashcart, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/screwdriver, /obj/item/wirecutters, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "gHV" = ( @@ -53010,7 +52909,7 @@ /area/station/supply/lobby) "hjE" = ( /obj/effect/spawner/random_spawners/oil_maybe, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "hka" = ( @@ -53359,7 +53258,7 @@ /area/station/engineering/atmos/control) "hqx" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft2) "hqL" = ( @@ -53447,7 +53346,7 @@ /area/station/medical/virology) "hts" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -53746,10 +53645,6 @@ /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "hAK" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/pipe/simple/hidden/cyan, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/structure/cable{ @@ -53757,6 +53652,7 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fore) "hAL" = ( @@ -53854,11 +53750,8 @@ /obj/structure/closet/crate/secure/bin, /obj/item/paper/crumpled, /obj/item/paper/crumpled, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/effect/decal/cleanable/dirt, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/aft2) "hCM" = ( @@ -53902,11 +53795,6 @@ icon_state = "neutralcorner" }, /area/station/hallway/secondary/bridge) -"hDE" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/two, -/turf/simulated/floor/plating, -/area/station/maintenance/asmaint) "hDK" = ( /turf/simulated/floor/plasteel, /area/station/supply/lobby) @@ -53946,7 +53834,7 @@ /area/station/science/robotics) "hEj" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_north) "hEo" = ( @@ -54111,7 +53999,7 @@ /area/station/engineering/control) "hFY" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "hGj" = ( @@ -55345,20 +55233,17 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/structure/cable{ d1 = 1; d2 = 4; icon_state = "1-4" }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_north) "igJ" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) @@ -55423,11 +55308,6 @@ }, /turf/simulated/floor/plasteel, /area/station/engineering/atmos) -"iip" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/port) "iiB" = ( /obj/docking_port/stationary{ dir = 8; @@ -56348,7 +56228,7 @@ /area/station/security/execution) "iBb" = ( /obj/structure/closet/secure_closet/personal, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/under/plasmaman/science, /obj/item/clothing/head/helmet/space/plasmaman/science, /obj/effect/spawner/random_spawners/cobweb_left_rare, @@ -56419,11 +56299,8 @@ /obj/structure/disposalpipe/broken{ dir = 1 }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/pipe/simple/hidden/cyan, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "iBR" = ( @@ -56545,7 +56422,7 @@ /obj/structure/closet/crate{ name = "Silver Crate" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "iGV" = ( @@ -57715,7 +57592,7 @@ /area/station/engineering/control) "jiR" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -57733,7 +57610,7 @@ /obj/effect/decal/cleanable/dirt, /obj/structure/rack, /obj/item/tank/internals/anesthetic, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/contraband/random{ pixel_y = 32 }, @@ -58131,7 +58008,7 @@ "juY" = ( /obj/structure/rack, /obj/item/poster/random_contraband, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "jvf" = ( @@ -58658,7 +58535,7 @@ /area/station/security/armory) "jGq" = ( /obj/structure/closet/crate/engineering/electrical, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "jGr" = ( @@ -59664,7 +59541,7 @@ /obj/item/stack/sheet/glass{ amount = 12 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "jXC" = ( @@ -59726,7 +59603,7 @@ /area/station/science/xenobiology) "jYH" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/cable{ d1 = 1; d2 = 8; @@ -59903,7 +59780,7 @@ /area/station/maintenance/aft2) "ken" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -60311,10 +60188,7 @@ /area/station/engineering/atmos) "kol" = ( /obj/effect/landmark/burnturf, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fore) "kpl" = ( @@ -60484,7 +60358,7 @@ /area/station/maintenance/engimaint) "ksa" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "ksb" = ( @@ -60827,22 +60701,10 @@ "kAP" = ( /obj/structure/closet/crate/trashcart, /obj/effect/turf_decal/delivery, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "kBa" = ( @@ -61196,10 +61058,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/medmaint) "kGN" = ( @@ -61238,7 +61097,7 @@ "kIc" = ( /obj/structure/rack, /obj/item/hand_labeler, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "kIi" = ( @@ -61509,7 +61368,7 @@ d2 = 8; icon_state = "4-8" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port) "kOw" = ( @@ -61763,7 +61622,7 @@ /area/station/security/permabrig) "kUz" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_south) "kUA" = ( @@ -62595,7 +62454,7 @@ /area/station/science/rnd) "ljT" = ( /obj/item/rack_parts, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -63164,7 +63023,7 @@ /area/station/maintenance/port) "lvJ" = ( /obj/structure/closet/secure_closet/personal, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/under/rank/rnd/scientist/skirt, /obj/item/clothing/suit/hooded/wintercoat/science, /obj/item/clothing/head/hooded/winterhood/science, @@ -63277,7 +63136,7 @@ /obj/machinery/atmospherics/pipe/simple/visible{ dir = 9 }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_north) "lAl" = ( @@ -63434,7 +63293,7 @@ /area/station/maintenance/fpmaint) "lCO" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft2) "lCQ" = ( @@ -63532,7 +63391,7 @@ /area/station/science/toxins/mixing) "lEM" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "lEN" = ( @@ -63593,7 +63452,7 @@ "lFM" = ( /obj/structure/closet/crate, /obj/item/cane, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "lGh" = ( @@ -63697,7 +63556,7 @@ /area/station/maintenance/fore) "lHy" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/item/clothing/gloves/color/white, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -63819,7 +63678,7 @@ /area/station/security/brig) "lJt" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "lJA" = ( @@ -64411,7 +64270,7 @@ /area/station/science/xenobiology) "lXi" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_rare, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) @@ -64422,7 +64281,7 @@ /obj/structure/rack, /obj/effect/decal/cleanable/dirt, /obj/item/clothing/suit/storage/hazardvest, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "caution" }, @@ -65390,7 +65249,7 @@ /area/station/maintenance/fore2) "mtr" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/aft2) @@ -65539,7 +65398,7 @@ "mxp" = ( /obj/structure/rack, /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft2) "mxw" = ( @@ -67336,7 +67195,7 @@ desc = "Takes you to a whole new level of thinking."; name = "Meta-Cider" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) @@ -67468,7 +67327,7 @@ "nhR" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/fsmaint) "nia" = ( @@ -68130,7 +67989,7 @@ /area/station/service/clown) "nwY" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/stripes/white/line{ dir = 10 }, @@ -68358,7 +68217,7 @@ /area/station/maintenance/fore) "nBf" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plating, /area/station/maintenance/aft2) @@ -68392,7 +68251,7 @@ /area/station/medical/reception) "nBW" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 5 }, @@ -68405,7 +68264,7 @@ /area/station/maintenance/apmaint) "nCh" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "whitepurple" @@ -68463,7 +68322,7 @@ /area/station/security/brig) "nDd" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_south) "nDi" = ( @@ -69001,7 +68860,7 @@ /area/station/supply/lobby) "nPD" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_frequent, /turf/simulated/floor/plating, /area/station/maintenance/fore) @@ -69443,11 +69302,6 @@ /obj/item/toy/plushie/carpplushie, /turf/space, /area/space/nearstation) -"oai" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/eight, -/turf/simulated/floor/plating, -/area/station/maintenance/fpmaint) "oaj" = ( /obj/structure/table/glass, /obj/structure/cable{ @@ -69613,10 +69467,6 @@ /turf/simulated/floor/bluegrid, /area/station/telecomms/chamber) "odT" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -69625,6 +69475,7 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_north) "odW" = ( @@ -70250,7 +70101,7 @@ }, /area/station/service/chapel/office) "oyN" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "oyP" = ( @@ -70562,15 +70413,12 @@ }, /area/station/engineering/gravitygenerator) "oFf" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/structure/cable{ d1 = 1; d2 = 8; icon_state = "1-8" }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_south) "oFn" = ( @@ -70709,7 +70557,7 @@ /area/station/engineering/controlroom) "oIs" = ( /obj/machinery/atmospherics/pipe/simple/hidden/cyan, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "oIT" = ( @@ -71032,7 +70880,7 @@ dir = 4 }, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitepurple" @@ -71258,7 +71106,7 @@ /area/station/legal/lawoffice) "oUa" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/sign/poster/contraband/random{ pixel_y = 32 }, @@ -71613,7 +71461,7 @@ "pcb" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "pcd" = ( @@ -71787,15 +71635,12 @@ /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "pes" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/structure/cable{ d1 = 1; d2 = 4; icon_state = "1-4" }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_south) "peF" = ( @@ -71811,7 +71656,7 @@ /obj/structure/table, /obj/item/storage/belt/utility, /obj/item/robotanalyzer, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/starboard2) "pfp" = ( @@ -72519,7 +72364,7 @@ /area/station/hallway/secondary/exit) "pvb" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/starboard2) "pvo" = ( @@ -73056,11 +72901,6 @@ icon_state = "darkred" }, /area/station/security/storage) -"pGU" = ( -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, -/turf/simulated/floor/plating, -/area/station/maintenance/fpmaint) "pHq" = ( /obj/structure/rack, /obj/item/clothing/head/helmet/riot{ @@ -73768,13 +73608,7 @@ }, /area/station/medical/chemistry) "pUh" = ( -/obj/item/storage/box/monkeycubes, -/obj/item/storage/box/monkeycubes/stokcubes, -/obj/item/storage/box/monkeycubes/neaeracubes, -/obj/structure/table/glass, -/obj/item/storage/box/monkeycubes/wolpincubes, -/obj/item/storage/box/monkeycubes/nian_worme_cubes, -/obj/item/storage/box/monkeycubes/farwacubes, +/obj/structure/closet/secure_closet/genetics, /turf/simulated/floor/grass/no_creep, /area/station/science/genetics) "pUq" = ( @@ -74418,7 +74252,7 @@ /area/station/maintenance/starboard2) "qiA" = ( /obj/structure/closet/crate/sci, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/xenobio_north) "qiI" = ( @@ -75201,11 +75035,8 @@ dir = 4 }, /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/maintenance, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/aft2) "qyo" = ( @@ -75281,7 +75112,7 @@ /area/station/maintenance/aft2) "qzT" = ( /obj/structure/closet/secure_closet/hydroponics, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "qAb" = ( @@ -75605,10 +75436,7 @@ }, /area/station/security/main) "qJN" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -75767,7 +75595,7 @@ /obj/structure/cable/cyan{ icon_state = "2-4" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fore) "qNV" = ( @@ -77010,7 +76838,7 @@ "roH" = ( /obj/structure/closet/crate/internals, /obj/effect/turf_decal/delivery/hollow, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "rpt" = ( @@ -77094,10 +76922,7 @@ /area/station/medical/exam_room) "rqC" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -77328,7 +77153,7 @@ "ruo" = ( /obj/structure/rack, /obj/item/stack/cable_coil/random, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "ruH" = ( @@ -77488,10 +77313,7 @@ /area/station/science/storage) "rxV" = ( /obj/structure/table/reinforced, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "caution" @@ -77885,7 +77707,7 @@ "rFg" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/crate/medical, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/aft) "rFq" = ( @@ -78057,7 +77879,7 @@ "rIT" = ( /obj/structure/table, /obj/item/geiger_counter, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "caution" @@ -78532,7 +78354,7 @@ /area/station/maintenance/turbine) "rRg" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -79966,22 +79788,6 @@ dir = 4 }, /obj/structure/closet/crate/trashcart, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/effect/landmark/burnturf, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -79994,6 +79800,10 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/aft) "szY" = ( @@ -81181,7 +80991,7 @@ /area/station/science/xenobiology) "sZC" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_frequent, /obj/machinery/light/small{ dir = 1 @@ -81294,7 +81104,7 @@ /area/station/science/research) "tba" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_rare, /obj/structure/cable{ d1 = 2; @@ -81342,7 +81152,7 @@ /area/station/science/xenobiology) "tca" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/atmospherics/pipe/simple/visible, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) @@ -81696,7 +81506,7 @@ d2 = 2; icon_state = "1-2" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "tjd" = ( @@ -82007,11 +81817,6 @@ }, /turf/space, /area/space/nearstation) -"tpw" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, -/turf/simulated/floor/plating, -/area/station/maintenance/port) "tpD" = ( /obj/effect/spawner/window, /turf/simulated/floor/plating, @@ -83065,7 +82870,7 @@ /area/station/service/hydroponics) "tTK" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/three, +/obj/effect/spawner/random/maintenance, /obj/structure/disposalpipe/segment/corner{ dir = 8 }, @@ -83488,10 +83293,7 @@ /obj/machinery/light/small{ dir = 4 }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "caution" @@ -83662,7 +83464,7 @@ "ufr" = ( /obj/structure/table, /obj/item/clothing/gloves/color/fyellow, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small{ dir = 8 @@ -83682,7 +83484,7 @@ d2 = 8; icon_state = "4-8" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/port2) "ufD" = ( @@ -84077,10 +83879,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/fore) "uoQ" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "upe" = ( @@ -84129,7 +83928,7 @@ "uqN" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/crate/medical, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -84787,7 +84586,7 @@ /area/station/hallway/primary/aft/north) "uIW" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/turf_decal/stripes/corner{ dir = 1 }, @@ -85103,7 +84902,7 @@ /area/station/engineering/break_room) "uNQ" = ( /obj/structure/closet/crate/medical, -/obj/effect/spawner/lootdrop/maintenance/two, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "uOg" = ( @@ -86306,7 +86105,7 @@ /area/station/service/hydroponics) "vmT" = ( /obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "vmW" = ( @@ -86357,7 +86156,7 @@ "vnt" = ( /obj/structure/closet/crate, /obj/effect/turf_decal/delivery, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "vnQ" = ( @@ -86616,7 +86415,7 @@ "vtY" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard) "vub" = ( @@ -86761,11 +86560,6 @@ /obj/effect/spawner/window/reinforced/grilled, /turf/simulated/floor/plating, /area/station/security/execution) -"vxi" = ( -/obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance/three, -/turf/simulated/floor/plating, -/area/station/maintenance/fpmaint) "vxx" = ( /obj/effect/spawner/random_spawners/grille_often, /turf/simulated/floor/plating, @@ -86822,25 +86616,6 @@ icon_state = "green" }, /area/station/security/permabrig) -"vzu" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/manifold/hidden/cyan{ - dir = 8 - }, -/obj/structure/disposalpipe/segment/corner{ - dir = 4 - }, -/obj/structure/cable{ - d1 = 2; - d2 = 4; - icon_state = "2-4" - }, -/turf/simulated/floor/plasteel, -/area/station/maintenance/fsmaint) "vzD" = ( /obj/machinery/iv_drip, /obj/structure/cable{ @@ -87061,7 +86836,7 @@ /area/station/security/processing) "vFm" = ( /obj/structure/closet/emcloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_left_rare, /turf/simulated/floor/plating, /area/station/maintenance/starboard) @@ -88053,7 +87828,7 @@ }, /obj/structure/table, /obj/item/storage/toolbox/electrical, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/effect/spawner/random_spawners/cobweb_right_rare, /turf/simulated/floor/plasteel, /area/station/maintenance/starboard2) @@ -88268,22 +88043,10 @@ /area/station/engineering/control) "wlo" = ( /obj/structure/closet/crate/trashcart, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "wlG" = ( @@ -88312,13 +88075,10 @@ /area/station/engineering/control) "wlT" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/structure/disposalpipe/segment/corner{ dir = 2 }, +/obj/effect/spawner/random/trash, /turf/simulated/floor/plating, /area/station/maintenance/aft) "wlW" = ( @@ -88425,12 +88185,9 @@ }, /area/station/science/research) "woK" = ( -/obj/effect/spawner/lootdrop{ - loot = list(/obj/item/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); - name = "trash spawner" - }, /obj/machinery/atmospherics/pipe/simple/hidden/cyan, /obj/item/pen, +/obj/effect/spawner/random/trash, /turf/simulated/floor/bluegrid, /area/station/maintenance/starboard) "woN" = ( @@ -88511,7 +88268,7 @@ /area/station/aisat) "wrO" = ( /obj/structure/closet/firecloset, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plasteel, /area/station/maintenance/starboard2) "wrW" = ( @@ -88902,7 +88659,7 @@ /area/station/maintenance/fore) "wzV" = ( /obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/machinery/light/small{ dir = 1 }, @@ -88979,7 +88736,7 @@ dir = 1 }, /obj/item/radio, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) "wBR" = ( @@ -89320,7 +89077,7 @@ /turf/simulated/floor/engine, /area/station/engineering/control) "wKQ" = ( -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /obj/structure/table, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 @@ -90455,7 +90212,7 @@ /area/station/security/permabrig) "xkc" = ( /obj/structure/closet, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) "xki" = ( @@ -90979,7 +90736,7 @@ d2 = 8; icon_state = "4-8" }, -/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/fpmaint) "xuG" = ( @@ -92165,6 +91922,22 @@ /obj/machinery/atmospherics/portable/canister/sleeping_agent, /turf/simulated/floor/plasteel, /area/station/engineering/atmos/control) +"xWu" = ( +/obj/effect/spawner/random/trash, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/manifold/hidden/cyan{ + dir = 8 + }, +/obj/structure/disposalpipe/segment/corner{ + dir = 4 + }, +/obj/structure/cable{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, +/turf/simulated/floor/plasteel, +/area/station/maintenance/fsmaint) "xWI" = ( /obj/structure/closet/secure_closet/brig, /turf/simulated/floor/plasteel{ @@ -105880,7 +105653,7 @@ uWq bBv dgb cdT -bTb +cPq bBz anE cdT @@ -106619,7 +106392,7 @@ adY cEr rbU xgb -vxi +aAP fuy adY aYb @@ -106914,7 +106687,7 @@ rvu wfa clT bTY -cQb +cLN bZP bcO bQy @@ -107160,7 +106933,7 @@ bHd aUu ckv cdT -tpw +cLN bTY bTY bTY @@ -108982,7 +108755,7 @@ xZa cCy crG crG -cSK +cFU crG jGq crG @@ -109245,7 +109018,7 @@ cFU cpD crG bZP -coj +clK gof crG bZP @@ -111230,7 +111003,7 @@ aOB aKy jGH aOB -oai +aAP aOB bfn eiR @@ -112263,7 +112036,7 @@ vyn vyn aEM aDz -pGU +aYb aAP aVl aVl @@ -113339,7 +113112,7 @@ bFw bTt cdT bqQ -iip +bRE bZP ceu tVy @@ -128541,7 +128314,7 @@ bUG nBW cPc rRg -hDE +das rOZ ljg fuC @@ -133074,7 +132847,7 @@ lEo qvN iBO aUx -vzu +xWu jJr tmq aBZ diff --git a/code/__DEFINES/_math.dm b/code/__DEFINES/_math.dm index 28c03149a60b..cd6c152f06d6 100644 --- a/code/__DEFINES/_math.dm +++ b/code/__DEFINES/_math.dm @@ -245,4 +245,8 @@ // Gives you the percent of two inputs #define PERCENT_OF(val1, val2) (val1 * (val2 / 100)) +///Returns an integer given a hex input, supports negative values "-ff". Skips preceding invalid characters. +#define hex2num(X) text2num(X, 16) + +/// Returns the hex value of a decimal number. len == length of returned string. #define num2hex(X, len) uppertext(num2text(X, len, 16)) diff --git a/code/__DEFINES/admin_defines.dm b/code/__DEFINES/admin_defines.dm index fd1ada925ef5..86afe81a3265 100644 --- a/code/__DEFINES/admin_defines.dm +++ b/code/__DEFINES/admin_defines.dm @@ -20,7 +20,6 @@ #define BANTYPE_ADMIN_PERMA 7 #define BANTYPE_ADMIN_TEMP 8 -//Please don't edit these values without speaking to Errorage first ~Carn //Admin Permissions #define R_BUILDMODE (1<<0) #define R_ADMIN (1<<1) @@ -40,11 +39,12 @@ #define R_PROCCALL (1<<15) #define R_VIEWRUNTIMES (1<<16) #define R_MAINTAINER (1<<17) +#define R_DEV_TEAM (1<<18) // Update the following two defines if you add more -#define R_MAXPERMISSION (1<<17) //This holds the maximum value for a permission. It is used in iteration, so keep it updated. +#define R_MAXPERMISSION (1<<18) // This holds the maximum value for a permission. It is used in iteration, so keep it updated. -#define R_HOST ((1<<18)-1) //17 bit bitmask, update me if we ever add more admin permissions. Sum of all permissions to allow easy setting. +#define R_HOST (~0) // Sum of all permissions to allow easy setting. #define ADMIN_QUE(user,display) "[display]" diff --git a/code/__DEFINES/chat.dm b/code/__DEFINES/chat.dm index 44bcdcd2a19d..8a0e4b37bf35 100644 --- a/code/__DEFINES/chat.dm +++ b/code/__DEFINES/chat.dm @@ -20,6 +20,7 @@ #define MESSAGE_TYPE_COMBAT "combat" #define MESSAGE_TYPE_ADMINCHAT "adminchat" #define MESSAGE_TYPE_MENTORCHAT "mentorchat" +#define MESSAGE_TYPE_DEVCHAT "devchat" #define MESSAGE_TYPE_EVENTCHAT "eventchat" #define MESSAGE_TYPE_ADMINLOG "adminlog" #define MESSAGE_TYPE_ATTACKLOG "attacklog" diff --git a/code/__DEFINES/mob_defines.dm b/code/__DEFINES/mob_defines.dm index c9725785dcde..58af2e1bb569 100644 --- a/code/__DEFINES/mob_defines.dm +++ b/code/__DEFINES/mob_defines.dm @@ -288,6 +288,8 @@ #define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0) +#define is_developer(user) (check_rights(R_DEV_TEAM, 0, (user)) != 0) + #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; // Locations diff --git a/code/__DEFINES/power_defines.dm b/code/__DEFINES/power_defines.dm index 15d4e1c1baa9..c7397a99d846 100644 --- a/code/__DEFINES/power_defines.dm +++ b/code/__DEFINES/power_defines.dm @@ -39,4 +39,10 @@ /// APC battery is at 100% #define APC_FULLY_CHARGED 2 -#define KW *1000 +#define KW * 1e3 +#define MW * 1e6 +#define GW * 1e9 + +#define KJ * 1e3 +#define MJ * 1e6 +#define GJ * 1e9 diff --git a/code/__DEFINES/speech_channels.dm b/code/__DEFINES/speech_channels.dm index 68067d21aec6..5e637ed7cd89 100644 --- a/code/__DEFINES/speech_channels.dm +++ b/code/__DEFINES/speech_channels.dm @@ -8,4 +8,5 @@ #define MENTOR_CHANNEL "Mentor" #define ADMIN_CHANNEL "Admin" #define DSAY_CHANNEL "Dsay" +#define DEV_CHANNEL "Dev" diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index f1b92bdfaa13..7fc2bee5074c 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -104,6 +104,10 @@ GLOBAL_PROTECT(log_end) if(GLOB.configuration.logging.adminchat_logging) rustg_log_write(GLOB.world_game_log, "MENTORSAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") +/proc/log_devsay(text, mob/speaker) + if(GLOB.configuration.logging.adminchat_logging) + rustg_log_write(GLOB.world_game_log, "DEVSAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") + /proc/log_ghostsay(text, mob/speaker) if(GLOB.configuration.logging.say_logging) rustg_log_write(GLOB.world_game_log, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 79047dfc6e63..0c172757ce39 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -883,3 +883,40 @@ for(var/key in input) UNTYPED_LIST_ADD(keys, key) return keys + +/** + * Given a list, return a copy where values without defined weights are given weight 1. + * For example, fill_with_ones(list(A, B=2, C)) = list(A=1, B=2, C=1) + * Useful for weighted random choices (loot tables, syllables in languages, etc.) + */ +/proc/fill_with_ones(list/list_to_pad) + if(!islist(list_to_pad)) + return list_to_pad + + var/list/final_list = list() + + for(var/key in list_to_pad) + if(list_to_pad[key]) + final_list[key] = list_to_pad[key] + else + final_list[key] = 1 + + return final_list + +/** + * Like pick_weight, but allowing for nested lists. + * + * For example, given the following list: + * list(A = 1, list(B = 1, C = 1)) + * A would have a 50% chance of being picked, + * and list(B, C) would have a 50% chance of being picked. + * If list(B, C) was picked, B and C would then each have a 50% chance of being picked. + * So the final probabilities would be 50% for A, 25% for B, and 25% for C. + * + * Weights should be integers. Entries without weights are assigned weight 1 (so unweighted lists can be used as well) + */ +/proc/pick_weight_recursive(list/list_to_pick) + var/result = pickweight(fill_with_ones(list_to_pick)) + while(islist(result)) + result = pickweight(fill_with_ones(result)) + return result diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index 57e0a7ee4257..a4a017f4518f 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -236,7 +236,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NPC_ZOMBIE "npc_zombie" // A trait for checking if a zombie should act like an NPC and attack #define TRAIT_ABSTRACT_HANDS "abstract_hands" // Mobs with this trait can only pick up abstract items. #define TRAIT_LANGUAGE_LOCKED "language_locked" // cant add/remove languages until removed (excludes babel because fuck everything i guess) -#define TRAIT_HAS_IV_BAG "iv_bag" // Used to check if there is an active IV bag. Currently blocks another IV bags from being inserted. #define TRAIT_PLAYING_CARDS "playing_cards" //***** MIND TRAITS *****/ diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index f883f0cec778..c77e07a55487 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -1,47 +1,12 @@ /* * Holds procs designed to change one type of value, into another. * Contains: - * hex2num & num2hex * file2list * angle2dir * angle2text * worldtime2text */ -//Returns an integer given a hex input -/proc/hex2num(hex) - if(!(istext(hex))) - return - - var/num = 0 - var/power = 0 - var/i = null - i = length(hex) - while(i > 0) - var/char = copytext(hex, i, i + 1) - switch(char) - if("0") - pass() // Do nothing - if("9", "8", "7", "6", "5", "4", "3", "2", "1") - num += text2num(char) * 16 ** power - if("a", "A") - num += 16 ** power * 10 - if("b", "B") - num += 16 ** power * 11 - if("c", "C") - num += 16 ** power * 12 - if("d", "D") - num += 16 ** power * 13 - if("e", "E") - num += 16 ** power * 14 - if("f", "F") - num += 16 ** power * 15 - else - return - power++ - i-- - return num - //Returns an integer value for R of R/G/B given a hex color input. /proc/color2R(hex) if(!(istext(hex))) @@ -187,6 +152,7 @@ if(rights & R_MENTOR) . += "[seperator]+MENTOR" if(rights & R_VIEWRUNTIMES) . += "[seperator]+VIEWRUNTIMES" if(rights & R_MAINTAINER) . += "[seperator]+MAINTAINER" + if(rights & R_DEV_TEAM) . += "[seperator]+DEV_TEAM" /proc/ui_style2icon(ui_style) switch(ui_style) diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm index f0ef03d554f5..0f54d48354e9 100644 --- a/code/__HELPERS/unique_ids.dm +++ b/code/__HELPERS/unique_ids.dm @@ -30,11 +30,22 @@ GLOBAL_LIST_EMPTY(uid_log) var/tag_backup = tag tag = null // Grab the raw ref, not the tag // num2text can output 8 significant figures max. If we go above 10 million UIDs in a round, shit breaks - unique_datum_id = "\ref[src]_[num2text(GLOB.next_unique_datum_id++, 8)]" + var/uid_number = num2text(GLOB.next_unique_datum_id++, 8) + unique_datum_id = "\ref[src]_[uid_number]" + md5_unique_datum_id = "[md5(unique_datum_id)]_[uid_number]" // Avoid ANY collision chance tag = tag_backup GLOB.uid_log[type]++ return unique_datum_id +/** + * Safety wrapper for getting MD5 UIDs + * + * This ensures it exists before reading it + */ +/datum/proc/MD5_UID() + UID() + return md5_unique_datum_id + /** * Locates a datum based off of the UID * diff --git a/code/_globalvars/lists/maint_loot_tables.dm b/code/_globalvars/lists/maint_loot_tables.dm new file mode 100644 index 000000000000..12640e2c35a4 --- /dev/null +++ b/code/_globalvars/lists/maint_loot_tables.dm @@ -0,0 +1,206 @@ +GLOBAL_LIST_INIT(maintenance_loot_tier_0, list( + list( + // Tools + /obj/effect/spawner/random/engineering/tools, + + // Materials + /obj/effect/spawner/random/engineering/materials, + ) = 6, + + list( + // Spawners for easily found items + /obj/effect/spawner/random/bureaucracy, + /obj/effect/spawner/random/dice, + /obj/effect/spawner/random/book, + + // Other worthless/easily found items + /obj/item/camera_film, + /obj/item/camera, + /obj/item/caution, + /obj/item/clothing/head/cone, + /obj/item/light/bulb, + /obj/item/light/tube, + /obj/item/poster/random_contraband, + /obj/item/poster/random_official, + /obj/item/reagent_containers/drinks/drinkingglass, + /obj/item/reagent_containers/glass/beaker/waterbottle, + /obj/item/reagent_containers/glass/beaker/waterbottle/empty, + /obj/item/scissors, + /obj/item/storage/box, + /obj/item/storage/fancy/crayons, + /obj/item/storage/fancy/matches, + ) = 2, + + list( + // Emergency items + /obj/item/extinguisher, + /obj/item/flashlight, + ) = 1, +)) + +GLOBAL_LIST_INIT(maintenance_loot_tier_1, list( + list( + // Sub-spawners + /obj/effect/spawner/random/engineering/toolbox, + /obj/effect/spawner/random/snacks, + + // Assemblies and cells + /obj/item/assembly/prox_sensor, + /obj/item/assembly/timer, + /obj/item/assembly/signaler, + /obj/item/assembly/voice, + /obj/item/assembly/voice/noise, + /obj/item/stock_parts/cell, + + // Clothing + /obj/item/clothing/glasses/sunglasses, + /obj/item/clothing/gloves/color/black, + /obj/item/clothing/gloves/color/fyellow, + /obj/item/clothing/gloves/color/yellow/fake, + /obj/item/clothing/head/hardhat, + /obj/item/clothing/head/hardhat/red, + /obj/item/clothing/head/that, + /obj/item/clothing/head/ushanka, + /obj/item/clothing/mask/gas, + /obj/item/clothing/shoes/black, + /obj/item/clothing/suit/storage/hazardvest, + /obj/item/clothing/under/color/black, + /obj/item/clothing/under/misc/vice, + + // Medical supplies / chemistry items + /obj/item/reagent_containers/dropper, + /obj/item/reagent_containers/glass/beaker, + /obj/item/reagent_containers/glass/beaker/large, + /obj/item/reagent_containers/syringe, + /obj/item/stack/medical/bruise_pack/advanced, + /obj/item/stack/medical/ointment/advanced, + + // Common items + /obj/item/bodybag, + /obj/item/cultivator, + /obj/item/flashlight/pen, + /obj/item/radio/off, + /obj/item/reagent_containers/drinks/mug, + /obj/item/reagent_containers/glass/bucket, + /obj/item/reagent_containers/spray/pestspray, + /obj/item/relic, + /obj/item/restraints/handcuffs/toy, + /obj/item/scratch, + /obj/item/seeds/ambrosia, + /obj/item/seeds/ambrosia/deus, + /obj/item/stack/sheet/cardboard, + /obj/item/stack/sheet/cloth, + /obj/item/storage/bag/plasticbag, + /obj/item/storage/box/cups, + /obj/item/storage/box/donkpockets, + /obj/item/storage/box/lights/mixed, + /obj/item/storage/fancy/cigarettes/dromedaryco, + /obj/item/tank/internals/emergency_oxygen, + /obj/item/tank/internals/emergency_oxygen/engi, + /obj/item/vending_refill/cola, + ) = 85, + + list( + /obj/item/storage/wallet, + /obj/item/storage/wallet/random, + ) = 5, + + list( + // Small chance of tier 1 stock parts + /obj/item/stock_parts/capacitor, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/matter_bin, + /obj/item/stock_parts/micro_laser, + /obj/item/stock_parts/scanning_module, + + // Coins + /obj/item/coin/silver, + /obj/item/coin/twoheaded, + ) = 2, +)) + +GLOBAL_LIST_INIT(maintenance_loot_tier_2, list( + list( + // Rarer items + /obj/effect/spawner/random/mod/maint, + /obj/item/clothing/glasses/meson, + /obj/item/clothing/head/welding, + /obj/item/crowbar/red, + /obj/item/storage/belt/utility, + ) = 45, + + list( + // Contraband and Syndicate items + /obj/item/ammo_box/magazine/m10mm, + /obj/item/clothing/mask/chameleon, + /obj/item/clothing/mask/chameleon/voice_change, + /obj/item/clothing/mask/gas/voice_modulator, + /obj/item/clothing/mask/gas/voice_modulator/chameleon, + /obj/item/clothing/shoes/chameleon/noslip, + /obj/item/clothing/suit/jacket/bomber/syndicate, + /obj/item/clothing/suit/storage/iaa/blackjacket/armored, + /obj/item/clothing/under/chameleon, + /obj/item/deck/cards/syndicate, + /obj/item/grenade/clown_grenade, + /obj/item/grenade/smokebomb, + /obj/item/gun/syringe/syndicate, + /obj/item/melee/knuckleduster/syndie, + /obj/item/mod/construction/broken_core, + /obj/item/multitool/ai_detect, + /obj/item/seeds/ambrosia/cruciatus, + /obj/item/soap/syndie, + /obj/item/stamp/chameleon, + /obj/item/storage/backpack/duffel/syndie/med/surgery_fake, + /obj/item/storage/backpack/satchel_flat, + /obj/item/storage/belt/military/traitor, + /obj/item/storage/fancy/cigarettes/cigpack_syndicate, + /obj/item/storage/pill_bottle/fakedeath, + /obj/item/storage/secure/briefcase/syndie, + /obj/item/storage/toolbox/syndicate, + /obj/item/suppressor, + /obj/item/weaponcrafting/receiver, + ) = 45, + + list( + // Health/repair kits + /obj/item/storage/firstaid/regular, + /obj/item/storage/firstaid/machine, + + // Rarer departmental items + /obj/item/reagent_scanner/adv, + /obj/item/robotanalyzer, + /obj/item/stack/nanopaste, + /obj/item/whetstone, + + // Other rare but useful items + /obj/item/radio/headset, + /obj/item/melee/knuckleduster, + ) = 3, +)) + +GLOBAL_LIST_INIT(maintenance_loot_tier_3, list( + list( + // Coveted items + /obj/item/clothing/gloves/color/yellow, + ) = 7, + + list( + // Rare Syndicate items + /obj/item/gun/projectile/automatic/pistol, + /obj/item/dnascrambler, + /obj/item/bio_chip_implanter/storage, + /obj/item/reagent_containers/spray/sticky_tar, + /obj/item/storage/box/syndie_kit/space, + ) = 3, +)) + +GLOBAL_LIST_INIT(maintenance_loot_tables, list( + list( + GLOB.maintenance_loot_tier_0 = 490, + GLOB.maintenance_loot_tier_1 = 390, + GLOB.maintenance_loot_tier_2 = 114, + GLOB.maintenance_loot_tier_3 = 6, + ) = 75, + + /obj/effect/spawner/random/trash = 25, +)) diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index e6b68fcbddae..f276bcc25abd 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -97,7 +97,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_I_WANT_BRAINS" = TRAIT_I_WANT_BRAINS, "TRAIT_ABSTRACT_HANDS" = TRAIT_ABSTRACT_HANDS, "TRAIT_LANGUAGE_LOCKED" = TRAIT_LANGUAGE_LOCKED, - "TRAIT_HAS_IV_BAG" = TRAIT_HAS_IV_BAG, "TRAIT_NON_INFECTIOUS_ZOMBIE" = TRAIT_NON_INFECTIOUS_ZOMBIE, "TRAIT_CANNOT_PULL" = TRAIT_CANNOT_PULL ), diff --git a/code/_onclick/hud/hud_datum.dm b/code/_onclick/hud/hud_datum.dm index 6aa1d4e8a8e8..f3e0b770f9a8 100644 --- a/code/_onclick/hud/hud_datum.dm +++ b/code/_onclick/hud/hud_datum.dm @@ -134,6 +134,7 @@ mymob.throw_icon = null mymob.healths = null mymob.healthdoll = null + mymob.staminas = null mymob.pullin = null mymob.nutrition_display = null diff --git a/code/controllers/configuration/sections/discord_configuration.dm b/code/controllers/configuration/sections/discord_configuration.dm index 4f34a7537070..c2fd17acfd65 100644 --- a/code/controllers/configuration/sections/discord_configuration.dm +++ b/code/controllers/configuration/sections/discord_configuration.dm @@ -11,13 +11,12 @@ var/mentor_role_id = "" /// List of all URLs for the main webhooks var/list/main_webhook_urls = list() - /// List of all URLs for the admin webhooks - var/list/mentor_webhook_urls = list() /// List of all URLs for the mentor webhooks + var/list/mentor_webhook_urls = list() + /// List of all URLs for the admin webhooks var/list/admin_webhook_urls = list() - /datum/configuration_section/discord_configuration/load_data(list/data) // Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line CONFIG_LOAD_BOOL(webhooks_enabled, data["enable_discord_webhooks"]) diff --git a/code/controllers/subsystem/SSstatpanel.dm b/code/controllers/subsystem/SSstatpanel.dm index 08840da95412..20804e50b5f2 100644 --- a/code/controllers/subsystem/SSstatpanel.dm +++ b/code/controllers/subsystem/SSstatpanel.dm @@ -142,6 +142,7 @@ SUBSYSTEM_DEF(statpanels) var/list/to_make = obj_window.atoms_to_imagify var/list/turf_items = list() var/i = 0 + var/client_uid = load_from.UID() for(var/atom/turf_item as anything in obj_window.atoms_to_show) // Limit what we send to the client's rendered section. i++ @@ -154,8 +155,13 @@ SUBSYSTEM_DEF(statpanels) if(existing_image == OBJ_IMAGE_LOADING) continue // We already have it. Success! + + // Store the cache the MD5'd UID for safety reasons + var/obj_m5_uid = turf_item.MD5_UID() + load_from.m5_uid_cache[obj_m5_uid] = turf_item.unique_datum_id + if(existing_image) - turf_items["[i]"] = list("[turf_item.name]", turf_item.UID(), SSassets.transport.get_asset_url(existing_image), existing_image) + turf_items["[i]"] = list("[turf_item.name]", obj_m5_uid, SSassets.transport.get_asset_url(existing_image), existing_image, client_uid) continue // Now, we're gonna queue image generation out of those refs to_make += turf_item diff --git a/code/controllers/subsystem/SSticker.dm b/code/controllers/subsystem/SSticker.dm index d262c69c6fd6..9defe6791281 100644 --- a/code/controllers/subsystem/SSticker.dm +++ b/code/controllers/subsystem/SSticker.dm @@ -819,7 +819,7 @@ SUBSYSTEM_DEF(ticker) /datum/controller/subsystem/ticker/proc/count_xenomorps() . = 0 - for(var/datum/mind/xeno_mind as anything in SSticker.mode.xenos) + for(var/datum/mind/xeno_mind in SSticker.mode.xenos) if(xeno_mind.current?.stat == DEAD) continue .++ diff --git a/code/datums/datum.dm b/code/datums/datum.dm index cf1715b7a878..56e35b452ed8 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -8,6 +8,8 @@ var/list/list/datum/callback/signal_procs var/var_edited = FALSE //Warranty void if seal is broken var/tmp/unique_datum_id = null + /// MD5'd version of the UID. Used for instances where we dont want to make clients aware of UIDs. + VAR_PRIVATE/tmp/md5_unique_datum_id = null // using VAR_PRIVATE means it cant be accessed outside of the MD5_UID() proc /// Used by SSprocessing var/isprocessing = FALSE diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 280213036183..0d1e158d9baf 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -466,6 +466,8 @@ "} + if(istype(D, /datum)) + log_admin("[key_name(usr)] opened VV for [D] ([D.UID()])") usr << browse(html, "window=variables[refid];size=475x650") #define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) diff --git a/code/datums/keybindings/communication_keybinds.dm b/code/datums/keybindings/communication_keybinds.dm index b3ddd289fda4..a3b95b8dd511 100644 --- a/code/datums/keybindings/communication_keybinds.dm +++ b/code/datums/keybindings/communication_keybinds.dm @@ -72,3 +72,8 @@ name = DSAY_CHANNEL keys = list("F10") required_rights = R_ADMIN + +/datum/keybinding/client/communication/devsay + name = DEV_CHANNEL + keys = list("F2") + required_rights = R_DEV_TEAM | R_ADMIN diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 5134398ef868..bb28197d6191 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -690,7 +690,7 @@ ) /datum/outfit/admin/solgov_rep - name = "Solar Federation Representative" + name = "Trans-Solar Federation Representative" uniform = /obj/item/clothing/under/solgov/rep back = /obj/item/storage/backpack/satchel @@ -720,12 +720,12 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) apply_to_card(I, H, get_all_centcom_access(), name, "lifetimeid") - I.assignment = "Solar Federation Representative" + I.assignment = "Trans-Solar Federation Representative" H.sec_hud_set_ID() /datum/outfit/admin/solgov - name = "Solar Federation Marine" + name = "TSF Marine" uniform = /obj/item/clothing/under/solgov suit = /obj/item/clothing/suit/armor/bulletproof back = /obj/item/storage/backpack/ert/solgov @@ -778,7 +778,7 @@ H.sec_hud_set_ID() /datum/outfit/admin/solgov/lieutenant - name = "Solar Federation Lieutenant" + name = "TSF Lieutenant" uniform = /obj/item/clothing/under/solgov/command head = /obj/item/clothing/head/beret/solgov/command glasses = /obj/item/clothing/glasses/night @@ -798,7 +798,7 @@ is_solgov_lieutenant = TRUE /datum/outfit/admin/solgov/elite - name = "Solar Federation Specops Marine" + name = "MARSOC Marine" uniform = /obj/item/clothing/under/solgov/elite suit = /obj/item/clothing/suit/space/hardsuit/ert/solgov head = null @@ -819,7 +819,7 @@ ) /datum/outfit/admin/solgov/elite/lieutenant - name = "Solar Federation Specops Lieutenant" + name = "MARSOC Lieutenant" uniform = /obj/item/clothing/under/solgov/command/elite suit = /obj/item/clothing/suit/space/hardsuit/ert/solgov/command head = null @@ -1508,7 +1508,7 @@ H.mind.AddSpell(S) /datum/outfit/admin/viper - name = "Solar Federation Viper Infiltrator" + name = "TSF Viper Infiltrator" uniform = /obj/item/clothing/under/solgov/viper back = /obj/item/storage/backpack/satchel diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 54d299509518..c399912d0d0e 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -6,7 +6,7 @@ GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","E name = "changeling" config_tag = "changeling" restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation General") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Trans-Solar Federation General") protected_species = list("Machine") required_players = 15 required_enemies = 1 diff --git a/code/game/gamemodes/cult/blood_magic.dm b/code/game/gamemodes/cult/blood_magic.dm index ce0699b4a03f..5ad1dd22dfe1 100644 --- a/code/game/gamemodes/cult/blood_magic.dm +++ b/code/game/gamemodes/cult/blood_magic.dm @@ -388,7 +388,7 @@ /obj/item/melee/blood_magic/Initialize(mapload, spell) . = ..() - if(has_source) + if(spell && has_source) source = spell uses = source.charges health_cost = source.health_cost diff --git a/code/game/gamemodes/cult/cult_mode.dm b/code/game/gamemodes/cult/cult_mode.dm index 97a7e9e12a09..a682eae42551 100644 --- a/code/game/gamemodes/cult/cult_mode.dm +++ b/code/game/gamemodes/cult/cult_mode.dm @@ -6,7 +6,7 @@ /datum/game_mode/cult name = "cult" config_tag = "cult" - restricted_jobs = list("Chaplain", "AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation General") + restricted_jobs = list("Chaplain", "AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Trans-Solar Federation General") protected_jobs = list() required_players = 30 required_enemies = 3 diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index 2d13e0c5c49e..9bee0cf6cf02 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -779,7 +779,7 @@ Congratulations! You are now trained for invasive xenobiology research!"} icon = 'icons/obj/abductor.dmi' icon_state = "mop_abductor" mopcap = 100 - origin_tech = "materials=3;engineering=3;abductor=3" + origin_tech = "materials=3;engineering=3;abductor=2" refill_rate = 50 refill_reagent = "water" mopspeed = 10 @@ -795,7 +795,7 @@ Congratulations! You are now trained for invasive xenobiology research!"} desc = "It's important to keep all the mysterious lights on a UFO functional when flying over backwater country." icon = 'icons/obj/abductor.dmi' icon_state = "lightreplacer_abductor" - origin_tech = "magnets=3;engineering=4;abductor=3" + origin_tech = "magnets=3;engineering=4;abductor=2" max_uses = 40 uses = 20 diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm index 8ef863e2dcbf..29af3fd38600 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm +++ b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm @@ -5,6 +5,9 @@ #define PULSEDEMON_SMES_DRAIN_MULTIPLIER 10 #define ALERT_CATEGORY_NOPOWER "pulse_nopower" #define ALERT_CATEGORY_NOREGEN "pulse_noregen" +/// Conversion ratio from Watt ticks to joules. +/// Should be a pulse demon's life tick length in seconds. +#define WATT_TICK_TO_JOULE 2 /mob/living/simple_animal/demon/pulse_demon name = "pulse demon" @@ -56,20 +59,20 @@ /// List of sounds that is picked from when the demon dies or is EMP'd. var/list/hurt_sounds = list("sound/voice/pdwail1.ogg", "sound/voice/pdwail2.ogg", "sound/voice/pdwail3.ogg") - /// Current quantity of power the demon currently holds, spent while purchasing, upgrading or using spells or upgrades. Use adjust_charge to modify this. + /// Current quantity of energy the demon currently holds (Joules), spent while purchasing, upgrading or using spells or upgrades. Use adjust_charge to modify this. var/charge = 1000 - /// Maximum quantity of power the demon can hold at once. + /// Maximum quantity of energy the demon can hold at once (Joules). var/maxcharge = 1000 - /// Book keeping for objective win conditions. + /// Book keeping for objective win conditions (Joules). var/charge_drained = 0 /// Controls whether the demon will drain power from sources. Toggled by a spell. var/do_drain = TRUE - /// Amount of power (in watts) to drain from power sources every Life tick. + /// Amount of power (Watts) to drain from power sources every Life tick. var/power_drain_rate = 1000 - /// Maximum value for power_drain_rate based on upgrades. + /// Maximum value for power_drain_rate based on upgrades. (Watts) var/max_drain_rate = 1000 - /// Amount of power (in watts) required to regenerate health. + /// Amount of power (Watts) required to regenerate health. var/power_per_regen = 1000 /// Amount of health lost per Life tick when the power requirement was not met. var/health_loss_rate = 5 @@ -90,7 +93,7 @@ /// The time it takes to hijack APCs and cyborgs. var/hijack_time = 30 SECONDS - /// The color of light the demon emits. The range of the light is proportional to charge. + /// The color of light the demon emits. The range of the light is proportional to energy stored. var/glow_color = "#bbbb00" /// Area being controlled, should be maintained as long as the demon does not move outside a container (APC, object, robot, bot). @@ -268,9 +271,9 @@ /mob/living/simple_animal/demon/pulse_demon/get_status_tab_items() var/list/status_tab_data = ..() . = status_tab_data - status_tab_data[++status_tab_data.len] = list("Charge:", "[format_si_suffix(charge)]W") - status_tab_data[++status_tab_data.len] = list("Maximum Charge:", "[format_si_suffix(maxcharge)]W") - status_tab_data[++status_tab_data.len] = list("Drained Charge:", "[format_si_suffix(charge_drained)]W") + status_tab_data[++status_tab_data.len] = list("Energy:", "[format_si_suffix(charge)]J") + status_tab_data[++status_tab_data.len] = list("Maximum Energy:", "[format_si_suffix(maxcharge)]J") + status_tab_data[++status_tab_data.len] = list("Drained Energy:", "[format_si_suffix(charge_drained)]J") status_tab_data[++status_tab_data.len] = list("Hijacked APCs:", "[length(hijacked_apcs)]") status_tab_data[++status_tab_data.len] = list("Drain Rate:", "[format_si_suffix(power_drain_rate)]W") status_tab_data[++status_tab_data.len] = list("Hijack Time:", "[hijack_time / 10] seconds") @@ -452,14 +455,16 @@ /mob/living/simple_animal/demon/pulse_demon/proc/drain_APC(obj/machinery/power/apc/A, multiplier = 1) if(A.being_hijacked) return PULSEDEMON_SOURCE_DRAIN_INVALID - var/amount_to_drain = clamp(A.cell.charge, 0, power_drain_rate * multiplier) - A.cell.use(min(amount_to_drain, maxcharge - charge)) // calculated seperately because the apc charge multiplier shouldn't affect the actual consumption + //CELLRATE is the conversion ratio between a watt tick and powercell energy storage units + var/amount_to_drain = clamp(A.cell.charge / GLOB.CELLRATE, 0, power_drain_rate * WATT_TICK_TO_JOULE * multiplier) + A.cell.use(min(amount_to_drain * GLOB.CELLRATE, maxcharge - charge)) // calculated seperately because the apc charge multiplier shouldn't affect the actual consumption return adjust_charge(amount_to_drain * PULSEDEMON_APC_CHARGE_MULTIPLIER) /mob/living/simple_animal/demon/pulse_demon/proc/drain_SMES(obj/machinery/power/smes/S, multiplier = 1) - var/amount_to_drain = clamp(S.charge, 0, power_drain_rate * multiplier * PULSEDEMON_SMES_DRAIN_MULTIPLIER) + //CELLRATE is the conversion ratio between a watt tick and powercell energy storage units. + var/amount_to_drain = clamp(S.charge / GLOB.CELLRATE, 0, power_drain_rate * WATT_TICK_TO_JOULE * multiplier * PULSEDEMON_SMES_DRAIN_MULTIPLIER) var/drained = adjust_charge(amount_to_drain) - S.charge -= drained + S.charge -= drained * GLOB.CELLRATE return drained /mob/living/simple_animal/demon/pulse_demon/Life(seconds, times_fired) @@ -862,3 +867,4 @@ #undef PULSEDEMON_PLATING_SPARK_CHANCE #undef PULSEDEMON_APC_CHARGE_MULTIPLIER #undef PULSEDEMON_SMES_DRAIN_MULTIPLIER +#undef WATT_TICK_TO_JOULE diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm index 1892a86ddd6d..f611824bcf15 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm +++ b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm @@ -13,9 +13,9 @@ clothes_req = FALSE action_background_icon_state = "bg_pulsedemon" var/locked = TRUE - var/unlock_cost = 1 KW - var/cast_cost = 1 KW - var/upgrade_cost = 1 KW + var/unlock_cost = 1 KJ + var/cast_cost = 1 KJ + var/upgrade_cost = 1 KJ var/requires_area = FALSE base_cooldown = 20 SECONDS level_max = 4 @@ -122,9 +122,9 @@ name = "Cable Hop" desc = "Jump to another cable in view." action_icon_state = "pd_cablehop" - unlock_cost = 15 KW - cast_cost = 5 KW - upgrade_cost = 75 KW + unlock_cost = 15 KJ + cast_cost = 5 KJ + upgrade_cost = 75 KJ /datum/spell/pulse_demon/cablehop/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) var/turf/O = get_turf(user) @@ -151,9 +151,9 @@ name = "Electromagnetic Tamper" desc = "Unlocks hidden programming in machines. Must be inside a hijacked APC to use." action_icon_state = "pd_emag" - unlock_cost = 50 KW - cast_cost = 20 KW - upgrade_cost = 200 KW + unlock_cost = 50 KJ + cast_cost = 20 KJ + upgrade_cost = 200 KJ requires_area = TRUE /datum/spell/pulse_demon/emagtamper/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) @@ -165,9 +165,9 @@ name = "Electromagnetic Pulse" desc = "Creates an EMP where you click. Be careful not to use it on yourself!" action_icon_state = "pd_emp" - unlock_cost = 50 KW - cast_cost = 10 KW - upgrade_cost = 200 KW + unlock_cost = 50 KJ + cast_cost = 10 KJ + upgrade_cost = 200 KJ requires_area = TRUE /datum/spell/pulse_demon/emp/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) @@ -179,9 +179,9 @@ name = "Overload Machine" desc = "Overloads a machine, causing it to explode." action_icon_state = "pd_overload" - unlock_cost = 300 KW - cast_cost = 50 KW - upgrade_cost = 500 KW + unlock_cost = 300 KJ + cast_cost = 50 KJ + upgrade_cost = 500 KJ requires_area = TRUE /datum/spell/pulse_demon/overload/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) @@ -206,8 +206,8 @@ name = "Remote Hijack" desc = "Remotely hijacks an APC." action_icon_state = "pd_remotehack" - unlock_cost = 15 KW - cast_cost = 10 KW + unlock_cost = 15 KJ + cast_cost = 10 KJ level_max = 0 base_cooldown = 3 SECONDS // you have to wait for the regular hijack time anyway @@ -224,9 +224,9 @@ name = "Remote Drain" desc = "Remotely drains a power source." action_icon_state = "pd_remotedrain" - unlock_cost = 5 KW + unlock_cost = 5 KJ cast_cost = 100 - upgrade_cost = 100 KW + upgrade_cost = 100 KJ /datum/spell/pulse_demon/remotedrain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) if(isapc(target)) @@ -295,8 +295,8 @@ desc = "Toggle whether you can move outside of cables or power sources." base_message = "move outside of cables." action_icon_state = "pd_toggle_exit" - unlock_cost = 100 KW - upgrade_cost = 300 KW + unlock_cost = 100 KJ + upgrade_cost = 300 KJ level_max = 3 /datum/spell/pulse_demon/toggle/can_exit_cable/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) @@ -391,27 +391,27 @@ if(PD_UPGRADE_HIJACK_SPEED) if(user.hijack_time <= 1 SECONDS) return -1 - cost = (100 / (user.hijack_time / (1 SECONDS))) * 20 KW + cost = (100 / (user.hijack_time / (1 SECONDS))) * 20 KJ if(PD_UPGRADE_DRAIN_SPEED) - if(user.max_drain_rate >= 500 KW) + if(user.max_drain_rate >= 500 KJ) return -1 cost = user.max_drain_rate * 15 if(PD_UPGRADE_MAX_HEALTH) if(user.maxHealth >= 200) return -1 - cost = user.maxHealth * 5 KW + cost = user.maxHealth * 5 KJ if(PD_UPGRADE_HEALTH_REGEN) if(user.health_regen_rate >= 100) return -1 - cost = user.health_regen_rate * 50 KW + cost = user.health_regen_rate * 50 KJ if(PD_UPGRADE_HEALTH_LOSS) if(user.health_loss_rate <= 1) return -1 - cost = (100 / user.health_loss_rate) * 20 KW + cost = (100 / user.health_loss_rate) * 20 KJ if(PD_UPGRADE_HEALTH_COST) if(user.power_per_regen <= 1) return -1 - cost = (100 / user.power_per_regen) * 50 KW + cost = (100 / user.power_per_regen) * 50 KJ if(PD_UPGRADE_MAX_CHARGE) cost = user.maxcharge else @@ -434,7 +434,7 @@ to_chat(user, "Pulse Demon upgrades:") for(var/upgrade in upgrade_descs) var/cost = calc_cost(user, upgrade) - to_chat(user, "[upgrade] ([cost == -1 ? "Fully Upgraded" : "[format_si_suffix(cost)]W"]) - [upgrade_descs[upgrade]]") + to_chat(user, "[upgrade] ([cost == -1 ? "Fully Upgraded" : "[format_si_suffix(cost)]J"]) - [upgrade_descs[upgrade]]") /datum/spell/pulse_demon/open_upgrades/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) var/upgrades = get_upgrades(user) @@ -461,10 +461,10 @@ to_chat(user, "You have upgraded your [choice], it now takes [user.hijack_time / (1 SECONDS)] second\s to hijack APCs.") if(PD_UPGRADE_DRAIN_SPEED) var/old = user.max_drain_rate - user.max_drain_rate = min(round(user.max_drain_rate * 1.5), 500 KW) + user.max_drain_rate = min(round(user.max_drain_rate * 1.5), 500 KJ) if(user.power_drain_rate == old) user.power_drain_rate = user.max_drain_rate - to_chat(user, "You have upgraded your [choice], you can now drain [format_si_suffix(user.max_drain_rate)]W per cycle.") + to_chat(user, "You have upgraded your [choice], you can now drain [format_si_suffix(user.max_drain_rate)]W.") if(PD_UPGRADE_MAX_HEALTH) user.maxHealth = min(round(user.maxHealth * 1.5), 200) to_chat(user, "You have upgraded your [choice], your max health is now [user.maxHealth].") @@ -480,7 +480,7 @@ to_chat(user, "Additionally, if you enable draining while on a cable, any excess power that would've been used regenerating will be added to your charge.") if(PD_UPGRADE_MAX_CHARGE) user.maxcharge = round(user.maxcharge * 2) - to_chat(user, "You have upgraded your [choice], you can now store [format_si_suffix(user.maxcharge)]W of charge.") + to_chat(user, "You have upgraded your [choice], you can now store [format_si_suffix(user.maxcharge)]J of energy.") else return FALSE return TRUE diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index 9dda5bec3f58..22180aa51768 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -2,7 +2,7 @@ name = "traitor" config_tag = "traitor" restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation General") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Trans-Solar Federation General") required_players = 0 required_enemies = 1 recommended_enemies = 4 diff --git a/code/game/gamemodes/trifecta/trifecta.dm b/code/game/gamemodes/trifecta/trifecta.dm index 67567be3a7a3..ab711b728cf9 100644 --- a/code/game/gamemodes/trifecta/trifecta.dm +++ b/code/game/gamemodes/trifecta/trifecta.dm @@ -6,7 +6,7 @@ /datum/game_mode/trifecta name = "Trifecta" config_tag = "trifecta" - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Solar Federation General") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Trans-Solar Federation General") restricted_jobs = list("Cyborg") secondary_restricted_jobs = list("AI") required_players = 25 diff --git a/code/game/gamemodes/vampire/traitor_vamp.dm b/code/game/gamemodes/vampire/traitor_vamp.dm index cdb9fa1036ed..1348b2e15936 100644 --- a/code/game/gamemodes/vampire/traitor_vamp.dm +++ b/code/game/gamemodes/vampire/traitor_vamp.dm @@ -2,7 +2,7 @@ name = "traitor_vampire" config_tag = "traitorvamp" traitors_possible = 3 //hard limit on traitors if scaling is turned off - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Solar Federation General") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Trans-Solar Federation General") restricted_jobs = list("Cyborg") secondary_restricted_jobs = list("AI", "Chaplain") required_players = 10 diff --git a/code/game/gamemodes/vampire/vampire_gamemode.dm b/code/game/gamemodes/vampire/vampire_gamemode.dm index 6e274c5b3a80..a9fefb3747a3 100644 --- a/code/game/gamemodes/vampire/vampire_gamemode.dm +++ b/code/game/gamemodes/vampire/vampire_gamemode.dm @@ -2,7 +2,7 @@ name = "vampire" config_tag = "vampire" restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Chaplain", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation General") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Chaplain", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Trans-Solar Federation General") protected_species = list("Machine") required_players = 15 required_enemies = 1 diff --git a/code/game/gamemodes/wizard/magic_tarot.dm b/code/game/gamemodes/wizard/magic_tarot.dm index 53e4185fefbd..f2e0dfdac8aa 100644 --- a/code/game/gamemodes/wizard/magic_tarot.dm +++ b/code/game/gamemodes/wizard/magic_tarot.dm @@ -189,7 +189,7 @@ flip() if(our_tarot) user.drop_item() - pre_activate(user) + pre_activate(user, user) return qdel(src) @@ -206,7 +206,7 @@ if(has_been_activated) return if(isliving(hit_atom) && our_tarot) - pre_activate(hit_atom) + pre_activate(hit_atom, locateUID(throwingdatum.thrower_uid)) return qdel(src) @@ -231,13 +231,15 @@ new /obj/effect/temp_visual/revenant(get_turf(src)) qdel(src) -/obj/item/magic_tarot_card/proc/pre_activate(mob/user) +/obj/item/magic_tarot_card/proc/pre_activate(mob/user, atom/movable/thrower) has_been_activated = TRUE forceMove(user) var/obj/effect/temp_visual/card_preview/tarot/draft = new /obj/effect/temp_visual/card_preview/tarot(user, "tarot_[our_tarot.card_icon]") user.vis_contents += draft user.visible_message("[user] holds up [src]!") addtimer(CALLBACK(our_tarot, TYPE_PROC_REF(/datum/tarot, activate), user), 0.5 SECONDS) + if(ismob(thrower) && our_tarot) + add_attack_logs(thrower, user, "[thrower] has activated [our_tarot.name] on [user]", ATKLOG_FEW) QDEL_IN(src, 0.6 SECONDS) /obj/effect/temp_visual/card_preview @@ -549,6 +551,8 @@ /datum/tarot/the_tower/activate(mob/living/target) var/obj/item/grenade/clusterbuster/ied/bakoom = new(get_turf(target)) + var/turf/bombturf = get_turf(target) + target.investigate_log("[key_name(target)] has been activated (either thrown at or used) on [target] at [bombturf.x],[bombturf.y],[bombturf.z]", INVESTIGATE_BOMB) // Yes, this is an atom proc. Suffering bakoom.prime() /// I'm sorry matt, this is very funny. diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index d16b6e912e23..31a15e5bb071 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -97,7 +97,7 @@ return get_all_centcom_access() + get_all_accesses() if("Special Operations Officer") return get_all_centcom_access() + get_all_accesses() - if("Solar Federation General") + if("Trans-Solar Federation General") return get_all_centcom_access() + get_all_accesses() if("Nanotrasen Navy Representative") return get_all_centcom_access() + get_all_accesses() @@ -388,13 +388,13 @@ /proc/get_all_solgov_jobs() return list( - "Sol Trader", - "Solar Federation Marine", - "Solar Federation Lieutenant", - "Solar Federation Specops Marine", - "Solar Federation Specops Lieutenant", - "Solar Federation Representative", - "Solar Federation General") + "Trans-Solar Federation Trader", + "TSF Marine", + "TSF Lieutenant", + "MARSOC Marine", + "MARSOC Lieutenant", + "Trans-Solar Federation Representative", + "Trans-Solar Federation General") /proc/get_all_soviet_jobs() return list( diff --git a/code/game/jobs/job/central.dm b/code/game/jobs/job/central.dm index 307b84c82895..137d1a4d0eb2 100644 --- a/code/game/jobs/job/central.dm +++ b/code/game/jobs/job/central.dm @@ -101,11 +101,11 @@ H.mind.offstation_role = TRUE /datum/job/ntspecops/solgovspecops - title = "Solar Federation General" + title = "Trans-Solar Federation General" outfit = /datum/outfit/job/ntspecops/solgovspecops /datum/outfit/job/ntspecops/solgovspecops - name = "Solar Federation General" + name = "Trans-Solar Federation General" uniform = /obj/item/clothing/under/rank/centcom/captain/solgov suit = /obj/item/clothing/suit/space/deathsquad/officer/solgov head = /obj/item/clothing/head/helmet/space/deathsquad/beret/solgov diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index d440e7624d3a..131bc83a1a38 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -72,6 +72,7 @@ kick_out_watchers() QDEL_NULL(assembly) QDEL_NULL(wires) + GLOB.cameranet.removeCamera(src) GLOB.cameranet.cameras -= src var/area/our_area = get_area(src) if(our_area) // We should probably send out the warning alarms if this doesn't exist, because this should always have an area! diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 1d6b8da9934e..6904c345779a 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -10,6 +10,7 @@ pass_flags = PASSTABLE var/obj/item/stock_parts/cell/charging = null var/chargelevel = -1 + /// Cell charge rate (Watts) var/charge_rate = 500 /obj/machinery/cell_charger/Initialize(mapload) diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm index 2c22054d06d9..de0db9f49def 100644 --- a/code/game/machinery/computer/HolodeckControl.dm +++ b/code/game/machinery/computer/HolodeckControl.dm @@ -232,7 +232,6 @@ icon = 'icons/turf/floors/carpet.dmi' icon_state = "carpet-255" base_icon_state = "carpet" - floor_tile = /obj/item/stack/tile/carpet smoothing_flags = SMOOTH_BITMASK smoothing_groups = list(SMOOTH_GROUP_TURF, SMOOTH_GROUP_CARPET) canSmoothWith = list(SMOOTH_GROUP_CARPET) @@ -265,7 +264,6 @@ pixel_x = -9 pixel_y = -9 layer = ABOVE_OPEN_TURF_LAYER - floor_tile = /obj/item/stack/tile/grass /turf/simulated/floor/holofloor/attackby(obj/item/W as obj, mob/user as mob, params) return diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 580373b5048b..f647b5e241b8 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -532,6 +532,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) modify.rank = t1 modify.assignment = t1 regenerate_id_name() + modify.RebuildHTML() return if("demote") if(modify.assignment == "Demoted") @@ -561,7 +562,9 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) modify.access = access modify.assignment = "Demoted" modify.icon_state = "id" + modify.rank = "Assistant" regenerate_id_name() + modify.RebuildHTML() return if("terminate") if(!has_idchange_access()) // because captain/HOP can use this even on dept consoles @@ -585,7 +588,9 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) job.current_positions-- modify.assignment = "Terminated" modify.access = list() + modify.rank = "Terminated" regenerate_id_name() + modify.RebuildHTML() return if("make_job_available") // MAKE ANOTHER JOB POSITION AVAILABLE FOR LATE JOINERS var/edit_job_target = params["job"] diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 1a4a3ca793b8..67b1fae2762d 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -721,6 +721,10 @@ to destroy them and players will be able to make replacements. icon_state = "engineering" build_path = /obj/machinery/autolathe/syndicate +/obj/item/circuitboard/autolathe/trapped + board_name = "Modified Autolathe" + build_path = /obj/machinery/autolathe/trapped + /obj/item/circuitboard/protolathe board_name = "Protolathe" icon_state = "science" diff --git a/code/game/machinery/tcomms/nttc.dm b/code/game/machinery/tcomms/nttc.dm index 8fbed17d8172..d2182137d897 100644 --- a/code/game/machinery/tcomms/nttc.dm +++ b/code/game/machinery/tcomms/nttc.dm @@ -59,13 +59,13 @@ "Nanotrasen Navy Representative" = "dsquadradio", "Research Officer" = "dsquadradio", "Special Operations Officer" = "dsquadradio", - "Sol Trader" = "dsquadradio", - "Solar Federation General" = "dsquadradio", - "Solar Federation Representative" = "dsquadradio", - "Solar Federation Specops Lieutenant" = "dsquadradio", - "Solar Federation Specops Marine" = "dsquadradio", - "Solar Federation Lieutenant" = "dsquadradio", - "Solar Federation Marine" = "dsquadradio", + "Trans-Solar Federation Trader" = "dsquadradio", + "Trans-Solar Federation General" = "dsquadradio", + "Trans-Solar Federation Representative" = "dsquadradio", + "MARSOC Lieutenant" = "dsquadradio", + "MARSOC Marine" = "dsquadradio", + "TSF Lieutenant" = "dsquadradio", + "TSF Marine" = "dsquadradio", "Supreme Commander" = "dsquadradio", "Thunderdome Overseer" = "dsquadradio", "VIP Guest" = "dsquadradio", @@ -140,7 +140,7 @@ /// List of CentComm jobs var/list/cc_jobs = list("Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Intel Officer", "Medical Officer", "Nanotrasen Navy Captain", "Nanotrasen Navy Representative", "Research Officer", "Supreme Commander", "Thunderdome Overseer") /// List of SolGov Marine jobs - var/list/tsf_jobs = list("Solar Federation Specops Lieutenant", "Solar Federation Specops Marine", "Solar Federation Lieutenant", "Solar Federation Marine", "Solar Federation Representative", "Solar Federation General", "VIP Guest") + var/list/tsf_jobs = list("MARSOC Lieutenant", "MARSOC Marine", "TSF Lieutenant", "TSF Marine", "Trans-Solar Federation Representative", "Trans-Solar Federation General", "VIP Guest") // Defined so code compiles and incase someone has a non-standard job var/job_class = "radio" // NOW FOR ACTUAL TOGGLES diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 002a392877c6..33093b2c11f1 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -462,14 +462,20 @@ icon_state = "bhole3" desc = "That's a nice station you have there. It'd be a shame if something happened to it." aSignal = /obj/item/assembly/signaler/anomaly/vortex + /// The timer that will give us an extra proccall of ripping the floors up + var/timer /obj/effect/anomaly/bhole/anomalyEffect() ..() if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen qdel(src) return + rip_and_tear() + // We queue up another `rip_and_tear` in a second, effectively making it tick once per second without having to switch this anomaly to another SS + timer = addtimer(CALLBACK(src, PROC_REF(rip_and_tear)), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_DELETE_ME) - grav(rand(0, 3), rand(2, 3), 100, 30) +/obj/effect/anomaly/bhole/proc/rip_and_tear() + grav(rand(1, 4), rand(2, 3), 100, 30) //Throwing stuff around! for(var/obj/O in range(3, src)) @@ -482,12 +488,15 @@ else O.ex_act(EXPLODE_HEAVY) -/obj/effect/anomaly/bhole/proc/grav(r, ex_act_force, pull_chance, turf_removal_chance) - for(var/t = -r, t < r, t++) - affect_coord(x + t, y - r, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x - t, y + r, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x + r, y + t, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x - r, y - t, ex_act_force, pull_chance, turf_removal_chance) +/obj/effect/anomaly/bhole/proc/grav(radius = 0, ex_act_force, pull_chance, turf_removal_chance) + if(radius <= 0 || prob(25)) // Base 25% chance of not damaging any floors or pulling mobs + return + + for(var/t in -radius to (radius - 1)) + affect_coord(x + t, y - radius, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x - t, y + radius, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x + radius, y + t, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x - radius, y - t, ex_act_force, pull_chance, turf_removal_chance) /obj/effect/anomaly/bhole/proc/affect_coord(x, y, ex_act_force, pull_chance, turf_removal_chance) //Get turf at coordinate diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 52150a68b92b..1e93d42a1040 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -48,6 +48,7 @@ /obj/effect/decal/cleanable/blood/Destroy() if(dry_timer) deltimer(dry_timer) + QDEL_NULL(weightless_image) return ..() /obj/effect/decal/cleanable/blood/update_icon() @@ -97,15 +98,12 @@ if(!locate(/obj/structure/grille/) in T && !locate(/obj/structure/window/) in T) qdel(src) //no free floating dried blood in space, thatd look weird -/obj/effect/decal/cleanable/blood/ex_act() - . = ..() - update_icon() - /obj/effect/decal/cleanable/blood/proc/splat(atom/AT) if(gravity_check) //only floating blood can splat :C return var/turf/T = get_turf(AT) - if(try_merging_decal(T)) + if(should_merge_decal(T)) + qdel(src) return if(loc != T) forceMove(T) //move to the turf to splatter on @@ -117,8 +115,6 @@ plane = initial(plane) update_icon() -/obj/effect/decal/cleanable/blood/try_merging_decal(turf/T) - ..() /obj/effect/decal/cleanable/blood/Process_Spacemove(movement_dir) if(gravity_check) @@ -139,11 +135,18 @@ /obj/effect/decal/cleanable/blood/Bump(atom/A, yes) + // this is to prevent double or triple bumps from calling splat after src is qdel'd. + // only god knows why this fixes the issue + if(yes) + return + if(gravity_check) return ..() + if(iswallturf(A) || istype(A, /obj/structure/window)) splat(A) return + else if(A.density) splat(get_turf(A)) return @@ -152,7 +155,7 @@ bloodyify_human(A) return - ..() + return ..() /obj/effect/decal/cleanable/blood/proc/bloodyify_human(mob/living/carbon/human/H) if(inertia_dir && H.inertia_dir == inertia_dir) //if they are moving the same direction we are, no collison diff --git a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm index cb0270cf3d02..9019d1393558 100644 --- a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm +++ b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm @@ -185,7 +185,8 @@ if(gravity_check) return var/turf/T = get_turf(A) - if(try_merging_decal(T)) + if(should_merge_decal(T)) + qdel(src) return if(loc != T) forceMove(T) diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index e2c8e72e9a1c..47e3693f95ff 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -87,12 +87,12 @@ /obj/effect/decal/cleanable/Initialize(mapload) . = ..() + if(should_merge_decal(loc)) + return INITIALIZE_HINT_QDEL var/datum/atom_hud/data/janitor/jani_hud = GLOB.huds[DATA_HUD_JANITOR] prepare_huds() jani_hud.add_to_hud(src) jani_hud_set_sign() - if(try_merging_decal()) - return TRUE if(random_icon_states && length(src.random_icon_states) > 0) src.icon_state = pick(src.random_icon_states) if(smoothing_flags) @@ -108,14 +108,13 @@ jani_hud.remove_from_hud(src) return ..() -/obj/effect/decal/cleanable/proc/try_merging_decal(turf/T) +/obj/effect/decal/cleanable/proc/should_merge_decal(turf/T) if(!T) T = loc if(isturf(T)) for(var/obj/effect/decal/cleanable/C in T) if(C != src && C.type == type && !QDELETED(C)) if(C.gravity_check && replace_decal(C)) - qdel(src) return TRUE return FALSE diff --git a/code/game/objects/effects/spawners/decorative_spawners.dm b/code/game/objects/effects/spawners/decorative_spawners.dm new file mode 100644 index 000000000000..7b200d627308 --- /dev/null +++ b/code/game/objects/effects/spawners/decorative_spawners.dm @@ -0,0 +1,131 @@ +/obj/effect/spawner/random_spawners + name = "random spawners" + icon = 'icons/effects/spawner_icons.dmi' + icon_state = "questionmark" + var/list/result = list( + /datum/nothing = 1, + /obj/effect/decal/cleanable/blood/splatter = 1, + /obj/effect/decal/cleanable/blood/oil = 1, + /obj/effect/decal/cleanable/fungus = 1) + var/spawn_inside = null + +// This needs to use New() instead of Initialize() because the thing it creates might need to be initialized too +// AA 2022-08-11: The above comment doesnt even make sense. If extra atoms are loaded during SSatoms.Initialize(), they still get initialised! +/obj/effect/spawner/random_spawners/New() + . = ..() + var/turf/T = get_turf(src) + if(!T) + stack_trace("Spawner placed in nullspace!") + return + randspawn(T) + +/obj/effect/spawner/random_spawners/proc/randspawn(turf/T) + var/thing_to_place = pickweight(result) + if(ispath(thing_to_place, /datum/nothing)) + // Nothing. + qdel(src) // See line 13, this needs moving to /Initialize() so we can use the qdel hint already + return + else if(ispath(thing_to_place, /turf)) + T.ChangeTurf(thing_to_place) + else + if(ispath(spawn_inside, /obj)) + var/obj/O = new thing_to_place(T) + var/obj/E = new spawn_inside(T) + O.forceMove(E) + else + new thing_to_place(T) + qdel(src) + +/obj/effect/spawner/random_spawners/blood_maybe + name = "blood maybe" + icon_state = "blood" + result = list( + /datum/nothing = 20, + /obj/effect/decal/cleanable/blood/splatter = 1) + +/obj/effect/spawner/random_spawners/blood_often + name = "blood often" + icon_state = "blood" + result = list( + /datum/nothing = 5, + /obj/effect/decal/cleanable/blood/splatter = 1) + +/obj/effect/spawner/random_spawners/oil_maybe + name = "oil maybe" + icon_state = "oil" + result = list( + /datum/nothing = 20, + /obj/effect/decal/cleanable/blood/oil = 1) + +/obj/effect/spawner/random_spawners/oil_often + name = "oil often" + icon_state = "oil" + result = list( + /datum/nothing = 5, + /obj/effect/decal/cleanable/blood/oil = 1) + +/obj/effect/spawner/random_spawners/cobweb_left_frequent + name = "cobweb left frequent" + icon_state = "cobwebl" + result = list( + /datum/nothing = 1, + /obj/effect/decal/cleanable/cobweb = 1) + +/obj/effect/spawner/random_spawners/cobweb_right_frequent + name = "cobweb right frequent" + icon_state = "cobwebr" + result = list( + /datum/nothing = 1, + /obj/effect/decal/cleanable/cobweb2 = 1) + +/obj/effect/spawner/random_spawners/cobweb_left_rare + name = "cobweb left rare" + icon_state = "cobwebl" + result = list( + /datum/nothing = 10, + /obj/effect/decal/cleanable/cobweb = 1) + +/obj/effect/spawner/random_spawners/cobweb_right_rare + name = "cobweb right rare" + icon_state = "cobwebr" + result = list( + /datum/nothing = 10, + /obj/effect/decal/cleanable/cobweb2 = 1) + +/obj/effect/spawner/random_spawners/dirt_frequent + name = "dirt frequent" + icon_state = "dirt" + result = list( + /datum/nothing = 1, + /obj/effect/decal/cleanable/dirt = 1) + +/obj/effect/spawner/random_spawners/dirt_often + name = "dirt often" + icon_state = "dirt" + result = list( + /datum/nothing = 5, + /obj/effect/decal/cleanable/dirt = 1) + +/obj/effect/spawner/random_spawners/dirt_maybe + name = "dirt maybe" + icon_state = "dirt" + result = list( + /datum/nothing = 7, + /obj/effect/decal/cleanable/dirt = 1) + +/obj/effect/spawner/random_spawners/fungus_maybe + name = "fungus maybe" + icon_state = "fungus" + color = "#D5820B" + result = list( + /datum/nothing = 7, + /obj/effect/decal/cleanable/fungus = 1) + +/obj/effect/spawner/random_spawners/fungus_probably + name = "fungus probably" + icon_state = "fungus" + color = "#D5820B" + result = list( + /datum/nothing = 1, + /obj/effect/decal/cleanable/fungus = 7) + diff --git a/code/game/objects/effects/spawners/random_spawners.dm b/code/game/objects/effects/spawners/depot_spawners.dm similarity index 58% rename from code/game/objects/effects/spawners/random_spawners.dm rename to code/game/objects/effects/spawners/depot_spawners.dm index 32b8052bd2f9..ac489f588c2d 100644 --- a/code/game/objects/effects/spawners/random_spawners.dm +++ b/code/game/objects/effects/spawners/depot_spawners.dm @@ -1,185 +1,6 @@ -/obj/effect/spawner/random_spawners - name = "random spawners" - icon = 'icons/effects/spawner_icons.dmi' - icon_state = "questionmark" - var/list/result = list( - /datum/nothing = 1, - /obj/effect/decal/cleanable/blood/splatter = 1, - /obj/effect/decal/cleanable/blood/oil = 1, - /obj/effect/decal/cleanable/fungus = 1) - var/spawn_inside = null - -// This needs to use New() instead of Initialize() because the thing it creates might need to be initialized too -// AA 2022-08-11: The above comment doesnt even make sense. If extra atoms are loaded during SSatoms.Initialize(), they still get initialised! -/obj/effect/spawner/random_spawners/New() - . = ..() - var/turf/T = get_turf(src) - if(!T) - stack_trace("Spawner placed in nullspace!") - return - randspawn(T) - -/obj/effect/spawner/random_spawners/proc/randspawn(turf/T) - var/thing_to_place = pickweight(result) - if(ispath(thing_to_place, /datum/nothing)) - // Nothing. - qdel(src) // See line 13, this needs moving to /Initialize() so we can use the qdel hint already - return - else if(ispath(thing_to_place, /turf)) - T.ChangeTurf(thing_to_place) - else - if(ispath(spawn_inside, /obj)) - var/obj/O = new thing_to_place(T) - var/obj/E = new spawn_inside(T) - O.forceMove(E) - else - new thing_to_place(T) - qdel(src) - -/obj/effect/spawner/random_spawners/blood_maybe - name = "blood maybe" - icon_state = "blood" - result = list( - /datum/nothing = 20, - /obj/effect/decal/cleanable/blood/splatter = 1) - -/obj/effect/spawner/random_spawners/blood_often - name = "blood often" - icon_state = "blood" - result = list( - /datum/nothing = 5, - /obj/effect/decal/cleanable/blood/splatter = 1) - -/obj/effect/spawner/random_spawners/oil_maybe - name = "oil maybe" - icon_state = "oil" - result = list( - /datum/nothing = 20, - /obj/effect/decal/cleanable/blood/oil = 1) - -/obj/effect/spawner/random_spawners/oil_often - name = "oil often" - icon_state = "oil" - result = list( - /datum/nothing = 5, - /obj/effect/decal/cleanable/blood/oil = 1) - -/obj/effect/spawner/random_spawners/proc/rustify(turf/T) - var/turf/simulated/wall/W = T - if(istype(W) && !W.rusted) - W.rust() - -/obj/effect/spawner/random_spawners/wall_rusted_probably - name = "rusted wall probably" - icon_state = "rust" - -/obj/effect/spawner/random_spawners/wall_rusted_probably/randspawn(turf/T) - if(prob(75)) - rustify(T) - qdel(src) - -/obj/effect/spawner/random_spawners/wall_rusted_maybe - name = "rusted wall maybe" - icon_state = "rust" - -/obj/effect/spawner/random_spawners/wall_rusted_maybe/randspawn(turf/T) - if(prob(25)) - rustify(T) - qdel(src) - -/obj/effect/spawner/random_spawners/wall_rusted_always - name = "rusted wall always" - icon_state = "rust" - -/obj/effect/spawner/random_spawners/wall_rusted_always/randspawn(turf/T) - rustify(T) - qdel(src) - -/obj/effect/spawner/random_spawners/cobweb_left_frequent - name = "cobweb left frequent" - icon_state = "cobwebl" - result = list( - /datum/nothing = 1, - /obj/effect/decal/cleanable/cobweb = 1) - -/obj/effect/spawner/random_spawners/cobweb_right_frequent - name = "cobweb right frequent" - icon_state = "cobwebr" - result = list( - /datum/nothing = 1, - /obj/effect/decal/cleanable/cobweb2 = 1) - -/obj/effect/spawner/random_spawners/cobweb_left_rare - name = "cobweb left rare" - icon_state = "cobwebl" - result = list( - /datum/nothing = 10, - /obj/effect/decal/cleanable/cobweb = 1) - -/obj/effect/spawner/random_spawners/cobweb_right_rare - name = "cobweb right rare" - icon_state = "cobwebr" - result = list( - /datum/nothing = 10, - /obj/effect/decal/cleanable/cobweb2 = 1) - -/obj/effect/spawner/random_spawners/dirt_frequent - name = "dirt frequent" - icon_state = "dirt" - result = list( - /datum/nothing = 1, - /obj/effect/decal/cleanable/dirt = 1) - -/obj/effect/spawner/random_spawners/dirt_often - name = "dirt often" - icon_state = "dirt" - result = list( - /datum/nothing = 5, - /obj/effect/decal/cleanable/dirt = 1) - -/obj/effect/spawner/random_spawners/dirt_maybe - name = "dirt maybe" - icon_state = "dirt" - result = list( - /datum/nothing = 7, - /obj/effect/decal/cleanable/dirt = 1) - -/obj/effect/spawner/random_spawners/fungus_maybe - name = "fungus maybe" - icon_state = "fungus" - color = "#D5820B" - result = list( - /datum/nothing = 7, - /obj/effect/decal/cleanable/fungus = 1) - -/obj/effect/spawner/random_spawners/fungus_probably - name = "fungus probably" - icon_state = "fungus" - color = "#D5820B" - result = list( - /datum/nothing = 1, - /obj/effect/decal/cleanable/fungus = 7) - -/obj/effect/spawner/random_spawners/mod - name = "MOD module spawner" - desc = "Modularize this, please." - icon_state = "circuit" - -/obj/effect/spawner/random_spawners/mod/maint - name = "maint MOD module spawner" - result = list( - /obj/item/mod/module/springlock = 2, - /obj/item/mod/module/balloon = 1, - /obj/item/mod/module/stamp = 1 - ) - - -// z6 DEPOT SPAWNERS - +/// Spawners for the Syndicate depot ruin. /obj/effect/spawner/random_spawners/syndicate - - // Turrets /obj/effect/spawner/random_spawners/syndicate/turret diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 069fd19b3b21..0daaeef79308 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -27,175 +27,6 @@ /obj/item/gun/projectile/automatic/pistol/deagle ) -/obj/effect/spawner/lootdrop/maintenance - name = "maintenance loot spawner (1 item)" - icon_state = "loot" - - //How to balance this table - //------------------------- - //The total added weight of all the entries should be (roughly) equal to the total number of lootdrops - //(take in account those that spawn more than one object!) - // - //While this is random, probabilities tells us that item distribution will have a tendency to look like - //the content of the weighted table that created them. - //The less lootdrops, the less even the distribution. - // - //If you want to give items a weight <1 you can multiply all the weights by 10 - // - //the "" entry will spawn nothing, if you increase this value, - //ensure that you balance it with more spawn points - - //table data: - //----------- - //aft maintenance: 24 items, 18 spots 2 extra (28/08/2014) - //asmaint: 16 items, 11 spots 0 extra (08/08/2014) - //asmaint2: 36 items, 26 spots 2 extra (28/08/2014) - //fpmaint: 5 items, 4 spots 0 extra (08/08/2014) - //fpmaint2: 12 items, 11 spots 2 extra (28/08/2014) - //fsmaint: 0 items, 0 spots 0 extra (08/08/2014) - //fsmaint2: 40 items, 27 spots 5 extra (28/08/2014) - //maintcentral: 2 items, 2 spots 0 extra (08/08/2014) - //port: 5 items, 5 spots 0 extra (08/08/2014) - loot = list( - /obj/item/bodybag = 10, - /obj/item/clothing/glasses/meson = 20, - /obj/item/clothing/glasses/sunglasses = 10, - /obj/item/clothing/gloves/color/yellow/fake = 15, - /obj/item/clothing/gloves/color/fyellow = 10, - /obj/item/clothing/gloves/color/yellow = 5, - /obj/item/clothing/gloves/color/black = 20, - /obj/item/clothing/head/hardhat = 10, - /obj/item/clothing/head/hardhat/red = 10, - /obj/item/clothing/head/that = 10, - /obj/item/clothing/head/ushanka = 10, - /obj/item/clothing/head/welding = 10, - /obj/item/clothing/mask/gas = 10, - /obj/item/clothing/suit/storage/hazardvest = 10, - /obj/item/clothing/under/misc/vice = 10, - /obj/item/assembly/prox_sensor = 40, - /obj/item/assembly/timer = 30, - /obj/item/flashlight = 40, - /obj/item/flashlight/pen = 10, - /obj/item/multitool = 20, - /obj/item/radio/off = 20, - /obj/item/t_scanner = 60, - /obj/item/stack/cable_coil = 40, - /obj/item/stack/cable_coil{amount = 5} = 60, - /obj/item/stack/medical/bruise_pack/advanced = 10, - /obj/item/stack/medical/ointment/advanced = 10, - /obj/item/stack/rods{amount = 10} = 80, - /obj/item/stack/rods{amount = 23} = 20, - /obj/item/stack/rods{amount = 50} = 10, - /obj/item/stack/sheet/cardboard = 20, - /obj/item/stack/sheet/metal{amount = 20} = 10, - /obj/item/stack/sheet/mineral/plasma = 10, - /obj/item/stack/sheet/rglass = 10, - /obj/item/stack/sheet/cloth{amount = 3} = 40, - /obj/item/book/manual/wiki/engineering_construction = 10, - /obj/item/book/manual/wiki/hacking = 10, - /obj/item/clothing/head/cone = 10, - /obj/item/geiger_counter = 30, - /obj/item/coin/silver = 10, - /obj/item/coin/twoheaded = 10, - /obj/item/poster/random_contraband = 10, - /obj/item/crowbar = 10, - /obj/item/crowbar/red = 10, - /obj/item/restraints/handcuffs/toy = 5, - /obj/item/extinguisher = 90, - /obj/item/hand_labeler = 10, - /obj/item/paper/crumpled = 10, - /obj/item/pen = 10, - /obj/item/cultivator = 10, - /obj/item/reagent_containers/spray/pestspray = 10, - /obj/item/stock_parts/cell = 30, - /obj/item/storage/belt/utility = 20, - /obj/item/storage/box = 20, - /obj/item/storage/box/cups = 10, - /obj/item/storage/box/donkpockets = 10, - /obj/item/storage/box/lights/mixed = 30, - /obj/item/storage/fancy/cigarettes/dromedaryco = 10, - /obj/item/storage/toolbox/mechanical = 10, - /obj/item/screwdriver = 30, - /obj/item/tank/internals/emergency_oxygen = 20, - /obj/item/tank/internals/emergency_oxygen/engi = 10, - /obj/item/vending_refill/cola = 10, - /obj/item/weldingtool = 30, - /obj/item/wirecutters = 10, - /obj/item/wrench = 40, - /obj/item/relic = 35, - /obj/item/weaponcrafting/receiver = 2, - /obj/item/clothing/shoes/black = 30, - /obj/item/seeds/ambrosia/deus = 10, - /obj/item/seeds/ambrosia = 20, - /obj/item/clothing/under/color/black = 30, - /obj/item/stack/tape_roll = 10, - /obj/item/storage/bag/plasticbag = 20, - /obj/item/storage/wallet = 20, - /obj/item/storage/wallet/random = 5, - /obj/item/scratch = 10, - /obj/item/caution = 10, - /obj/item/mod/construction/broken_core = 4, - /obj/effect/spawner/random_spawners/mod/maint = 10, - /obj/item/melee/knuckleduster = 10, - ////////////////CONTRABAND STUFF////////////////// - /obj/item/grenade/clown_grenade = 3, - /obj/item/grenade/smokebomb = 3, - /obj/item/seeds/ambrosia/cruciatus = 3, - /obj/item/gun/projectile/automatic/pistol = 1, - /obj/item/ammo_box/magazine/m10mm = 4, - /obj/item/soap/syndie = 7, - /obj/item/gun/syringe/syndicate = 2, - /obj/item/suppressor = 4, - /obj/item/clothing/under/chameleon = 2, - /obj/item/stamp/chameleon = 2, - /obj/item/clothing/shoes/chameleon/noslip = 5, - /obj/item/clothing/mask/chameleon = 4, - /obj/item/clothing/mask/chameleon/voice_change = 2, - /obj/item/clothing/mask/gas/voice_modulator = 2, - /obj/item/clothing/mask/gas/voice_modulator/chameleon = 2, - /obj/item/dnascrambler = 1, - /obj/item/storage/backpack/satchel_flat = 2, - /obj/item/storage/toolbox/syndicate = 2, - /obj/item/storage/backpack/duffel/syndie/med/surgery_fake = 2, - /obj/item/storage/belt/military/traitor = 2, - /obj/item/storage/box/syndie_kit/space = 2, - /obj/item/multitool/ai_detect = 2, - /obj/item/bio_chip_implanter/storage = 1, - /obj/item/deck/cards/syndicate = 2, - /obj/item/storage/secure/briefcase/syndie = 2, - /obj/item/storage/fancy/cigarettes/cigpack_syndicate = 2, - /obj/item/storage/pill_bottle/fakedeath = 2, - /obj/item/clothing/suit/jacket/bomber/syndicate = 5, - /obj/item/clothing/suit/storage/iaa/blackjacket/armored = 2, // More armored than bomber and has pockets, so it is rarer - /obj/item/melee/knuckleduster/syndie = 2, - /obj/item/reagent_containers/spray/sticky_tar = 1, - "" = 61 // This should be a decently high number for chances where no loot will spawn - ) - -/obj/effect/spawner/lootdrop/maintenance/Initialize(mapload) - if(HAS_TRAIT(SSstation, STATION_TRAIT_EMPTY_MAINT) && prob(50)) - return qdel(src) - if(HAS_TRAIT(SSstation, STATION_TRAIT_FILLED_MAINT) && prob(50)) - lootcount = min(lootcount * 2, 12) - . = ..() - - -/obj/effect/spawner/lootdrop/maintenance/two - name = "maintenance loot spawner (2 items)" - icon_state = "doubleloot" - lootcount = 2 - -/obj/effect/spawner/lootdrop/maintenance/three - name = "maintenance loot spawner (3 items)" - icon_state = "moreloot" - lootcount = 3 - -/obj/effect/spawner/lootdrop/maintenance/eight - name = "maintenance loot spawner (8 items)" - icon_state = "megaloot" - lootcount = 8 - - /// for ruins /obj/effect/spawner/lootdrop/crate_spawner name = "lootcrate spawner" diff --git a/code/game/objects/effects/spawners/mess_spawners.dm b/code/game/objects/effects/spawners/mess_spawners.dm index 9f97a15782c9..751613e9e111 100644 --- a/code/game/objects/effects/spawners/mess_spawners.dm +++ b/code/game/objects/effects/spawners/mess_spawners.dm @@ -194,6 +194,9 @@ var/mess_count = rand(5, 10) for(var/i in 1 to mess_count) var/area/target_area = findEventArea() + if(!target_area) + log_debug("Failed to generate themed messes: No valid event areas were found.") + return var/list/turfs = get_area_turfs(target_area) while(length(turfs)) var/turf/T = pick_n_take(turfs) diff --git a/code/game/objects/effects/spawners/random/engineering_spawners.dm b/code/game/objects/effects/spawners/random/engineering_spawners.dm new file mode 100644 index 000000000000..d9978dc606b8 --- /dev/null +++ b/code/game/objects/effects/spawners/random/engineering_spawners.dm @@ -0,0 +1,54 @@ +/obj/effect/spawner/random/engineering + icon = 'icons/effects/random_spawners.dmi' + icon_state = "wrench" + +/obj/effect/spawner/random/engineering/tools + name = "Tool spawner" + loot = list( + /obj/item/wrench = 2, + /obj/item/wirecutters = 2, + /obj/item/screwdriver = 2, + /obj/item/crowbar = 2, + /obj/item/weldingtool = 2, + /obj/item/stack/cable_coil = 2, + /obj/item/analyzer = 2, + /obj/item/t_scanner = 2, + /obj/item/geiger_counter = 2, + /obj/item/multitool = 1, + ) + +/obj/effect/spawner/random/engineering/materials + name = "Materials spawner" + icon_state = "metal" + loot = list( + list( + /obj/item/stack/rods, + /obj/item/stack/sheet/metal, + /obj/item/stack/sheet/glass, + /obj/item/stack/sheet/rglass, + /obj/item/stack/sheet/wood, + ) = 8, + + list( + /obj/item/stack/sheet/plastic, + /obj/item/stack/sheet/plasteel, + /obj/item/stack/sheet/mineral/plasma, + ) = 2, + ) + +/obj/effect/spawner/random/engineering/materials/make_item(spawn_loc, type_path_to_make) + var/obj/item/stack/item = ..() + if(istype(item)) + item.amount = rand(1, 10) + item.update_icon() + + return item + +/obj/effect/spawner/random/engineering/toolbox + name = "Toolbox spawner" + icon_state = "toolbox" + loot = list( + /obj/item/storage/toolbox/mechanical, + /obj/item/storage/toolbox/electrical, + /obj/item/storage/toolbox/emergency + ) diff --git a/code/game/objects/effects/spawners/random/food_spawners.dm b/code/game/objects/effects/spawners/random/food_spawners.dm new file mode 100644 index 000000000000..069859f2ef52 --- /dev/null +++ b/code/game/objects/effects/spawners/random/food_spawners.dm @@ -0,0 +1,20 @@ +/obj/effect/spawner/random/snacks + name = "snacks spawner" + icon = 'icons/effects/random_spawners.dmi' + icon_state = "donkpocket_single" + loot = list( + list( + /obj/item/food/candy/candybar, + /obj/item/reagent_containers/drinks/dry_ramen, + /obj/item/food/chips, + /obj/item/food/twimsts, + /obj/item/food/sosjerky, + /obj/item/food/no_raisin, + /obj/item/food/pistachios, + /obj/item/food/spacetwinkie, + /obj/item/food/cheesiehonkers, + /obj/item/food/tastybread, + ) = 5, + + /obj/item/food/stroopwafel = 1, + ) diff --git a/code/game/objects/effects/spawners/random/maint_loot_spawners.dm b/code/game/objects/effects/spawners/random/maint_loot_spawners.dm new file mode 100644 index 000000000000..2ad64ece299b --- /dev/null +++ b/code/game/objects/effects/spawners/random/maint_loot_spawners.dm @@ -0,0 +1,19 @@ +#define TRAIT_CHANCE_DELTA 25 + +/obj/effect/spawner/random/maintenance + name = "Maintenance loot spawner" + spawn_loot_chance = 65 + spawn_random_offset_max_pixels = 8 + +/obj/effect/spawner/random/maintenance/Initialize(mapload) + loot = GLOB.maintenance_loot_tables + spawn_loot_count = rand(2, 4) + + if(HAS_TRAIT(SSstation, STATION_TRAIT_EMPTY_MAINT)) + spawn_loot_chance -= TRAIT_CHANCE_DELTA + else if(HAS_TRAIT(SSstation, STATION_TRAIT_FILLED_MAINT)) + spawn_loot_chance += TRAIT_CHANCE_DELTA + + . = ..() + +#undef TRAIT_CHANCE_DELTA diff --git a/code/game/objects/effects/spawners/random/misc_spawners.dm b/code/game/objects/effects/spawners/random/misc_spawners.dm new file mode 100644 index 000000000000..7d4467767c23 --- /dev/null +++ b/code/game/objects/effects/spawners/random/misc_spawners.dm @@ -0,0 +1,92 @@ +/obj/effect/spawner/random/dice + loot = list( + /obj/item/dice/d4, + /obj/item/dice/d6, + /obj/item/dice/d8, + /obj/item/dice/d10, + /obj/item/dice/d12, + ) + +/obj/effect/spawner/random/dice/Initialize() + . = ..() + spawn_loot_count = rand(1, 2) + +/obj/effect/spawner/random/bureaucracy + icon = 'icons/effects/random_spawners.dmi' + icon_state = "folder" + name = "bureaucracy spawner" + loot = list( + /obj/item/hand_labeler, + /obj/item/hand_labeler_refill, + /obj/item/stack/tape_roll, + /obj/item/paper_bin, + /obj/item/pen, + /obj/item/pen/blue, + /obj/item/pen/red, + /obj/item/folder/blue, + /obj/item/folder/red, + /obj/item/folder/yellow, + /obj/item/clipboard, + ) + +/obj/effect/spawner/random/book + icon = 'icons/effects/random_spawners.dmi' + icon_state = "book" + name = "book spawner" + loot = list( + /obj/item/book/manual/atmospipes, + /obj/item/book/manual/barman_recipes, + /obj/item/book/manual/detective, + /obj/item/book/manual/engineering_particle_accelerator, + /obj/item/book/manual/engineering_singularity_safety, + /obj/item/book/manual/evaguide, + /obj/item/book/manual/hydroponics_pod_people, + /obj/item/book/manual/medical_cloning, + /obj/item/book/manual/research_and_development, + /obj/item/book/manual/ripley_build_and_repair, + /obj/item/book/manual/supermatter_engine, + /obj/item/book/manual/wiki/botanist, + /obj/item/book/manual/wiki/chef_recipes, + /obj/item/book/manual/wiki/engineering_construction, + /obj/item/book/manual/wiki/engineering_guide, + /obj/item/book/manual/wiki/experimentor, + /obj/item/book/manual/wiki/faxes, + /obj/item/book/manual/wiki/hacking, + /obj/item/book/manual/wiki/hydroponics, + /obj/item/book/manual/wiki/robotics_cyborgs, + /obj/item/book/manual/wiki/security_space_law, + /obj/item/book/manual/wiki/security_space_law/black, + /obj/item/book/manual/wiki/sop_command, + /obj/item/book/manual/wiki/sop_engineering, + /obj/item/book/manual/wiki/sop_general, + /obj/item/book/manual/wiki/sop_legal, + /obj/item/book/manual/wiki/sop_medical, + /obj/item/book/manual/wiki/sop_science, + /obj/item/book/manual/wiki/sop_security, + /obj/item/book/manual/wiki/sop_service, + /obj/item/book/manual/wiki/sop_supply, + /obj/item/book/manual/zombie_manual, + ) + +/obj/effect/spawner/random/book/record_item(type_path_to_make) + SSblackbox.record_feedback("tally", "random_spawners", 1, "[/obj/item/book]") + +/obj/effect/spawner/random/mod/maint + name = "maint MOD module spawner" + loot = list( + /obj/item/mod/module/springlock = 2, + /obj/item/mod/module/balloon = 1, + /obj/item/mod/module/stamp = 1 + ) + +/obj/effect/spawner/random/janitor/supplies + icon = 'icons/effects/random_spawners.dmi' + icon_state = "mopbucket" + name = "janitorial supplies spawner" + loot = list( + /obj/item/storage/box/mousetraps, + /obj/item/storage/box/lights/tubes, + /obj/item/storage/box/lights/mixed, + /obj/item/storage/box/lights/bulbs, + ) + diff --git a/code/game/objects/effects/spawners/random/random_spawner.dm b/code/game/objects/effects/spawners/random/random_spawner.dm new file mode 100644 index 000000000000..91814b9bdfb5 --- /dev/null +++ b/code/game/objects/effects/spawners/random/random_spawner.dm @@ -0,0 +1,143 @@ +/** + * Base class for all random spawners. + */ +/obj/effect/spawner/random + icon = 'icons/effects/spawner_icons.dmi' + icon_state = "loot" + layer = OBJ_LAYER + /// Stops persistent lootdrop spawns from being shoved into lockers + anchored = TRUE + /// A list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect) + var/list/loot + /// The subtypes AND type to combine with the loot list + var/loot_type_path + /// The subtypes (this excludes the provided path) to combine with the loot list + var/loot_subtype_path + /// How many items will be spawned + var/spawn_loot_count = 1 + /// If the same item can be spawned twice + var/spawn_loot_double = TRUE + /// Whether the items should be distributed to offsets 0,1,-1,2,-2,3,-3.. This overrides pixel_x/y on the spawner itself + var/spawn_loot_split = FALSE + /// The pixel x/y divider offsets for spawn_loot_split (spaced 1 pixel apart by default) + var/spawn_loot_split_pixel_offsets = 2 + /// Whether the spawner should spawn all the loot in the list + var/spawn_all_loot = FALSE + /// The chance for the spawner to create loot (ignores spawn_loot_count) + var/spawn_loot_chance = 100 + /// Determines how big of a range (in tiles) we should scatter things in. + var/spawn_scatter_radius = 0 + /// Whether the items should have a random pixel_x/y offset (maximum offset distance is ± spawn_random_offset_max_pixels for x/y) + var/spawn_random_offset = FALSE + /// Maximum offset distance for random pixel offsets. + var/spawn_random_offset_max_pixels = 16 + /// Whether the spawned items should be rotated randomly. + var/spawn_random_angle = FALSE + +// Brief explanation: +// Rather then setting up and then deleting spawners, we block all atomlike setup +// and do the absolute bare minimum +// This is with the intent of optimizing mapload +// TODO: Bring this optimization up one level if possible +/obj/effect/spawner/random/Initialize(mapload) + SHOULD_CALL_PARENT(FALSE) + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE + spawn_loot() + return INITIALIZE_HINT_QDEL + +///If the spawner has any loot defined, randomly picks some and spawns it. Does not cleanup the spawner. +/obj/effect/spawner/random/proc/spawn_loot(lootcount_override) + if(!prob(spawn_loot_chance)) + return + + var/list/spawn_locations = get_spawn_locations(spawn_scatter_radius) + var/spawn_loot_count = isnull(lootcount_override) ? src.spawn_loot_count : lootcount_override + + if(spawn_all_loot) + spawn_loot_count = INFINITY + spawn_loot_double = FALSE + + if(loot_type_path) + loot += typesof(loot_type_path) + + if(loot_subtype_path) + loot += subtypesof(loot_subtype_path) + + if(length(loot)) + var/loot_spawned = 0 + var/pixel_divider = FLOOR(spawn_random_offset_max_pixels / spawn_loot_split_pixel_offsets, 1) + while((spawn_loot_count-loot_spawned) && length(loot)) + var/lootspawn = pick_weight_recursive(loot) + if(!spawn_loot_double) + loot.Remove(lootspawn) + if(lootspawn) + var/turf/spawn_loc = loc + if(spawn_scatter_radius > 0 && length(spawn_locations)) + spawn_loc = pick(spawn_locations) + + var/atom/movable/spawned_loot = make_item(spawn_loc, lootspawn) + spawned_loot.setDir(dir) + + if(!spawn_loot_split && !spawn_random_offset) + if(pixel_x != 0) + spawned_loot.pixel_x = pixel_x + if(pixel_y != 0) + spawned_loot.pixel_y = pixel_y + else if(spawn_random_offset) + spawned_loot.pixel_x = rand(-spawn_random_offset_max_pixels, spawn_random_offset_max_pixels) + spawned_loot.pixel_y = rand(-spawn_random_offset_max_pixels, spawn_random_offset_max_pixels) + else if(spawn_loot_split) + if(loot_spawned) + var/column = FLOOR(loot_spawned / pixel_divider, 1) + spawned_loot.pixel_x = spawn_loot_split_pixel_offsets * (loot_spawned % pixel_divider) + (column * spawn_loot_split_pixel_offsets) + spawned_loot.pixel_y = spawn_loot_split_pixel_offsets * (loot_spawned % pixel_divider) + loot_spawned++ + +/** + * Makes the actual item related to our spawner. + * + * spawn_loc - where are we spawning it? + * type_path_to_make - what are we spawning? + **/ +/obj/effect/spawner/random/proc/make_item(spawn_loc, type_path_to_make) + var/result = new type_path_to_make(spawn_loc) + + record_item(type_path_to_make) + + var/atom/item = result + if(spawn_random_angle && istype(item)) + item.transform = turn(item.transform, rand(0, 360)) + + return result + +/obj/effect/spawner/random/proc/record_item(type_path_to_make) + if(ispath(type_path_to_make, /obj/effect)) + return + + SSblackbox.record_feedback("tally", "random_spawners", 1, "[type_path_to_make]") + +///If the spawner has a spawn_scatter_radius set, this creates a list of nearby turfs available that are in view and have an unblocked line to them. +/obj/effect/spawner/random/proc/get_spawn_locations(radius) + var/list/scatter_locations = list() + + if(!radius) + return scatter_locations + + for(var/turf/turf_in_view in view(radius, get_turf(src))) + if(!isfloorturf(turf_in_view)) + continue + if(!has_unblocked_line(turf_in_view)) + continue + + scatter_locations += turf_in_view + + return scatter_locations + +/obj/effect/spawner/random/proc/has_unblocked_line(destination) + for(var/turf/potential_blockage as anything in get_line(get_turf(src), destination)) + if(!is_blocked_turf(potential_blockage, exclude_mobs = TRUE)) + continue + return FALSE + return TRUE diff --git a/code/game/objects/effects/spawners/random/readme.md b/code/game/objects/effects/spawners/random/readme.md new file mode 100644 index 000000000000..e65d0f520087 --- /dev/null +++ b/code/game/objects/effects/spawners/random/readme.md @@ -0,0 +1,425 @@ +# Random Spawners + +## About + +Random spawners are an organized tool primarily for mapping to enhance replayability. The spawners can create objects, effects, and structures with different tweakable settings to get the desired outcome. You can make a spawner determine direction, rarity, number of items to spawn, pixel spacing between items, and even spread it over a large tile radius. This lets you control the atmosphere of a location. You could for instance spawn different piles of trash in maint or spawn decoration items for a room to give it more randomized flavor. The choice is yours! + +*(note the audience of this README is directed towards mappers who lack knowledge of coding)* + +## Variables + +The following variables are defined in `code/game/objects/effects/spawners/random/random.dm` that control how a spawner works. + +- `loot` - a list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect) +- `loot_type_path` - this combines the subtypes AND type list with the loot list +- `loot_subtype_path` - this combines ONLY the subtypes (excludes the `loot_subtype_path`) with the loot list +- `spawn_loot_count` - how many items will be spawned +- `spawn_loot_double` - if the same item can be spawned twice from the loot list +- `spawn_loot_split` - whether the items should be distributed to offsets 0,1,-1,2,-2,3,-3.. This overrides pixel_x/y on the spawner itself +- `spawn_all_loot` - whether the spawner should spawn all the loot in the list (ignores `spawn_loot_count`) +- `spawn_loot_chance` - the chance for the spawner to create loot (ignores `spawn_loot_count`) +- `spawn_scatter_radius` - determines how big of a range (in tiles) we should scatter things in + +These variables are set to the following default values for the base `random.dm` object that all objects inherit from: + +```dm + /// these three loot values are all empty + var/list/loot + var/loot_type_path + var/loot_subtype_path + + var/spawn_loot_count = 1 // by default one item will be selected from the loot list + var/spawn_loot_double = TRUE // by default duplicate items CAN be spawned from the loot list + var/spawn_loot_split = FALSE // by default items will NOT spread out on the same tile + var/spawn_all_loot = FALSE // by default the spawner will only spawn the number of items set in spawn_loot_count + var/spawn_loot_chance = 100 // by default the spawner has a 100% chance to spawn the item(s) + var/spawn_scatter_radius = 0 // by default the spawner will spawn the items ONLY on the tile it is on +``` + +However there are some categories that overwrite these default values so pay attention to the folder or category you group your spawner in. For instance the `obj/effect/spawner/random/techstorage` category overwrites the `spawn_all_loot` and the `spawn_loot_split` variables. + +```dm +// Tech storage circuit board spawners +/obj/effect/spawner/random/techstorage + name = "generic circuit board spawner" + spawn_loot_split = TRUE + spawn_all_loot = TRUE +``` + +This means any spawner you create under the techstorage will also have those variables set to that by default. This can be overridden quite easily just be resetting the variables back to the normal state like so: + +```dm +/obj/effect/spawner/random/techstorage/data_disk + name = "data disk spawner" + spawn_all_loot = FALSE // now our loot won't all be spawned + loot = list( + /obj/item/disk/data = 49, + /obj/item/disk/nuclear/fake/obvious = 1, + ) +``` + +## Template + +All the random spawners follow the same template format to keep things consistent and unison. + +```dm +/obj/effect/spawner/random/INSERT_SPAWNER_GROUP/INSERT_SPAWNER_NAME + name = "INSERT_SPAWNER_NAME spawner" + loot = list( + /obj/item/PATH/INSERT_OBJ_1, + /obj/item/PATH/INSERT_OBJ_2, + /obj/item/PATH/INSERT_OBJ_3, + ) +``` + +All the capitalized code is the parts where you are supposed to swap out with your objects like so: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture, + /obj/item/stack/medical/mesh, + /obj/item/stack/medical/gauze, + ) +``` + +Find the path to different objects and add them to the list but try to be consistent with the types of the object and the spawner. For example a medical spawner shouldn't have a emag in the loot list. (use an antag spawner for that instead!) + +## Probability + +Be aware that the `loot` list uses a *weighted chance* formula to determine probability. So if there are no numbers set in the `loot` list then each object defaults to 1 and has the same probability to be selected. For our above example for the `minor_healing` spawner each medical item has a 1/3 chance to be spawned. But if we rearranged the values to this: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture = 2, + /obj/item/stack/medical/mesh = 1, + /obj/item/stack/medical/gauze = 1, + ) +``` + +Then now `suture` has a 50% chance of being spawned (2/4), `mesh` has a 25% chance of being spawned (1/4), and `gauze` also has a 25% chance of being spawned (1/4). If we add another item into the mix then we get the following: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture = 2, + /obj/item/stack/medical/mesh = 1, + /obj/item/stack/medical/gauze = 1, + /obj/item/reagent_containers/syringe = 1, + ) +``` + +Suture is 40% (2/5), Mesh is 20% (1/5), Gauze is 20% (1/5), and Syringe is 20% (1/5). A weighted list has the advantage of not needing to update every item in the list when adding a new item. If the list was based on a straight percent values, then each new item would require to manually go and edit ALL the items in the list. For big lists that would become very tedious. This is why we use weighted lists to determine probability! + +## Style + +Here are some simple guidelines that you should stick to when making a new spawner: + +### If ALL the items have the same chance, we should not set a weighted value to the item + +Do not put `/obj/item/ = 1` unless other items have different spawn chances + +Good: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture = 2, + /obj/item/stack/medical/mesh = 1, + /obj/item/stack/medical/gauze = 1, + /obj/item/reagent_containers/syringe = 1, + ) +``` + +Also Good: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture, + /obj/item/stack/medical/mesh, + /obj/item/stack/medical/gauze, + /obj/item/reagent_containers/syringe, + ) +``` + +Bad: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture = 1, + /obj/item/stack/medical/mesh = 1, + /obj/item/stack/medical/gauze = 1, + /obj/item/reagent_containers/syringe = 1, + ) +``` + + +### Sort the list from highest probability to lowest + +Sort from top to bottom. The rarest items for your spawner should be at the bottom of the list. + +Good: + +```dm +/obj/effect/spawner/random/contraband/armory + name = "armory loot spawner" + loot = list( + /obj/item/gun/ballistic/automatic/pistol = 8, + /obj/item/gun/ballistic/shotgun/automatic/combat = 5, + /obj/item/storage/box/syndie_kit/throwing_weapons = 3, + /obj/item/grenade/clusterbuster/teargas = 2, + /obj/item/grenade/clusterbuster = 2, + /obj/item/gun/ballistic/automatic/pistol/deagle = 1, + /obj/item/gun/ballistic/revolver/mateba = 1, + ) +``` + +Bad: + +```dm +/obj/effect/spawner/random/contraband/armory + name = "armory loot spawner" + loot = list( + /obj/item/storage/box/syndie_kit/throwing_weapons = 3, + /obj/item/gun/ballistic/automatic/pistol = 8, + /obj/item/gun/ballistic/revolver/mateba = 1, + /obj/item/grenade/clusterbuster/teargas = 2, + /obj/item/gun/ballistic/automatic/pistol/deagle = 1, + /obj/item/grenade/clusterbuster = 2, + /obj/item/gun/ballistic/shotgun/automatic/combat = 5, + ) +``` + +### Always put the `loot` list at the bottom of your spawner + +This is just to keep things organized. + +Good: + +```dm +/obj/effect/spawner/random/food_or_drink/donkpockets + name = "donk pocket box spawner" + spawn_loot_double = FALSE + loot = list( + /obj/item/storage/box/donkpockets/donkpocketspicy, + /obj/item/storage/box/donkpockets/donkpocketteriyaki, + /obj/item/storage/box/donkpockets/donkpocketpizza, + /obj/item/storage/box/donkpockets/donkpocketberry, + /obj/item/storage/box/donkpockets/donkpockethonk, + ) +``` + +Bad: + +```dm +/obj/effect/spawner/random/food_or_drink/donkpockets + name = "donk pocket box spawner" + loot = list( + /obj/item/storage/box/donkpockets/donkpocketspicy, + /obj/item/storage/box/donkpockets/donkpocketteriyaki, + /obj/item/storage/box/donkpockets/donkpocketpizza, + /obj/item/storage/box/donkpockets/donkpocketberry, + /obj/item/storage/box/donkpockets/donkpockethonk, + ) + spawn_loot_double = FALSE +``` + +### Always put a comma at the last item in the `loot` list + +This will make it easier for people to add items to your spawner later without getting frustrating code errors. + +Good: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture, + /obj/item/stack/medical/mesh, + /obj/item/stack/medical/gauze, + ) +``` + +Bad: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture, + /obj/item/stack/medical/mesh, + /obj/item/stack/medical/gauze // if someone adds an item to the list later it will cause an error + ) +``` + +### Keep the same tab formatting for the `loot` list (unless there is only one item) + +Again, this is just good code organization. If there is only one item, then encase that item in `loot = list(item)` + +Good: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture, + /obj/item/stack/medical/mesh, + /obj/item/stack/medical/gauze, + ) +``` + +Also Good: + +```dm +/obj/effect/spawner/random/structure/crate_abandoned + icon = 'icons/effects/landmarks_static.dmi' + icon_state = "loot_site" + spawn_loot_chance = 20 + loot = list(/obj/structure/closet/crate/secure/loot) +``` + +Bad: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list( + /obj/item/stack/medical/suture, + /obj/item/stack/medical/mesh, + /obj/item/stack/medical/gauze, + ) +``` + +Also Bad: + +```dm +/obj/effect/spawner/random/medical/minor_healing + name = "minor healing spawner" + loot = list(/obj/item/stack/medical/suture, + /obj/item/stack/medical/mesh, + /obj/item/stack/medical/gauze,) +``` + +### Try to keep the total combined weight of your `loot` list to sane values (Aim for 5, 10, 20, 50, or 100) + +This makes the math probability easier for people to calculate. (this is recommended, but not always possible) + +Good: + +```dm +/obj/effect/spawner/random/trash/cigbutt + name = "cigarette butt spawner" + loot = list( + /obj/item/cigbutt = 65, + /obj/item/cigbutt/roach = 20, + /obj/item/cigbutt/cigarbutt = 15, + ) +``` + +Also Good: + +```dm +/obj/effect/spawner/random/trash/botanical_waste + name = "botanical waste spawner" + loot = list( + /obj/item/grown/bananapeel = 6, + /obj/item/grown/corncob = 3, + /obj/item/food/grown/bungopit = 1, + ) +``` + +Bad: + +```dm +/obj/effect/spawner/random/entertainment/money_large + name = "large money spawner" + loot = list( + /obj/item/stack/spacecash/c1 = 521, + /obj/item/stack/spacecash/c10 = 378, + /obj/item/stack/spacecash/c20 = 212, + /obj/item/stack/spacecash/c50 = 205, + /obj/item/stack/spacecash/c100 = 71, + /obj/item/stack/spacecash/c200 = 60, + /obj/item/stack/spacecash/c500 = 57, + /obj/item/stack/spacecash/c1000 = 41, + /obj/item/stack/spacecash/c10000 = 12, + ) +``` + +### Do not put empty items in the loot list + +Instead use the `spawn_loot_chance` var to control the chance for the spawner to spawn nothing. + +Good: + +```dm +/obj/effect/spawner/random/structure/crate_abandoned + name = "locked crate spawner" + spawn_loot_chance = 20 + loot = list(/obj/structure/closet/crate/secure/loot) +``` + + +Bad: + +```dm +/obj/effect/spawner/lootdrop/crate_spawner + name = "lootcrate spawner" + loot = list( + "" = 80 + /obj/structure/closet/crate/secure/loot = 20, + ) +``` + +### Avoid making a spawner that is a duplicate + +We don't want copy-cat spawners that are almost identical. Instead merge spawners together if possible. + +Good: + +```dm +/obj/effect/spawner/random/contraband/armory + name = "armory loot spawner" + icon_state = "pistol" + loot = list( + /obj/item/gun/ballistic/automatic/pistol = 8, + /obj/item/gun/ballistic/shotgun/automatic/combat = 5, + /obj/item/storage/box/syndie_kit/throwing_weapons = 3, + /obj/item/grenade/clusterbuster/teargas = 2, + /obj/item/grenade/clusterbuster = 2, + /obj/item/gun/ballistic/automatic/pistol/deagle, + /obj/item/gun/ballistic/revolver/mateba, + ) +``` + + +Bad: + +```dm +/obj/effect/spawner/lootdrop/armory_contraband + loot = list( + /obj/item/gun/ballistic/automatic/pistol = 8, + /obj/item/gun/ballistic/shotgun/automatic/combat = 5, + /obj/item/gun/ballistic/automatic/pistol/deagle, + /obj/item/gun/ballistic/revolver/mateba + ) + +/obj/effect/spawner/lootdrop/armory_contraband/metastation + loot = list( + /obj/item/gun/ballistic/automatic/pistol = 8, + /obj/item/gun/ballistic/shotgun/automatic/combat = 5, + /obj/item/storage/box/syndie_kit/throwing_weapons = 3, + /obj/item/gun/ballistic/automatic/pistol/deagle, + /obj/item/gun/ballistic/revolver/mateba + ) + +``` diff --git a/code/game/objects/effects/spawners/random/trash_spawners.dm b/code/game/objects/effects/spawners/random/trash_spawners.dm new file mode 100644 index 000000000000..776a387c0b79 --- /dev/null +++ b/code/game/objects/effects/spawners/random/trash_spawners.dm @@ -0,0 +1,75 @@ +/// Food trash spawner, for when you specifically want it to look like someone +/// didn't clean up after themselves after lunch. +/obj/effect/spawner/random/food_trash + icon = 'icons/effects/random_spawners.dmi' + icon_state = "tray" + name = "Food trash spawner" + loot = list( + /obj/item/trash/bowl, + /obj/item/trash/candle, + /obj/item/trash/candy, + /obj/item/trash/cheesie, + /obj/item/trash/chips, + /obj/item/trash/fried_vox, + /obj/item/trash/gum, + /obj/item/trash/liquidfood, + /obj/item/trash/pistachios, + /obj/item/trash/plate, + /obj/item/trash/popcorn, + /obj/item/trash/popsicle_stick, + /obj/item/trash/raisins, + /obj/item/trash/semki, + /obj/item/trash/snack_bowl, + /obj/item/trash/sosjerky, + /obj/item/trash/spacetwinkie, + /obj/item/trash/syndi_cakes, + /obj/item/trash/tastybread, + /obj/item/trash/tray, + /obj/item/trash/twimsts, + /obj/item/trash/waffles, + ) + + spawn_random_angle = TRUE + +/obj/effect/spawner/random/food_trash/record_item(type_path_to_make) + SSblackbox.record_feedback("tally", "random_spawners", 1, "[/obj/item/trash]") + +/obj/effect/spawner/random/trash + icon = 'icons/effects/random_spawners.dmi' + icon_state = "trash" + + name = "Trash spawner" + loot = list( + // Food litter often + /obj/effect/spawner/random/food_trash = 8, + + // Some regular trash + list( + /obj/item/broken_bottle, + /obj/item/cigbutt, + /obj/item/cigbutt/roach, + /obj/item/flashlight/flare/glowstick/used, + /obj/item/flashlight/flare/used, + /obj/item/paper/crumpled, + /obj/item/shard, + /obj/item/trash/tapetrash, + ) = 5, + + // Ammo casings rarely + list( + /obj/item/ammo_casing/c10mm, + /obj/item/trash/spentcasing, + ) = 1, + ) + + // TODO: Random spawner scatter behavior doesn't work well with items in + // containers or on dense objects like racks. Fix up so we can scatter trash. + spawn_random_angle = TRUE + spawn_random_offset = TRUE + spawn_random_offset_max_pixels = 8 + +/obj/effect/spawner/random/trash/record_item(type_path_to_make) + if(istype(type_path_to_make, /obj/effect/spawner)) + return + + SSblackbox.record_feedback("tally", "random_spawners", 1, "[/obj/item/trash]") diff --git a/code/game/objects/effects/spawners/turf_spawners.dm b/code/game/objects/effects/spawners/turf_spawners.dm new file mode 100644 index 000000000000..eeaf554b506a --- /dev/null +++ b/code/game/objects/effects/spawners/turf_spawners.dm @@ -0,0 +1,30 @@ +/obj/effect/spawner/random_spawners/proc/rustify(turf/T) + var/turf/simulated/wall/W = T + if(istype(W) && !W.rusted) + W.rust() + +/obj/effect/spawner/random_spawners/wall_rusted_probably + name = "rusted wall probably" + icon_state = "rust" + +/obj/effect/spawner/random_spawners/wall_rusted_probably/randspawn(turf/T) + if(prob(75)) + rustify(T) + qdel(src) + +/obj/effect/spawner/random_spawners/wall_rusted_maybe + name = "rusted wall maybe" + icon_state = "rust" + +/obj/effect/spawner/random_spawners/wall_rusted_maybe/randspawn(turf/T) + if(prob(25)) + rustify(T) + qdel(src) + +/obj/effect/spawner/random_spawners/wall_rusted_always + name = "rusted wall always" + icon_state = "rust" + +/obj/effect/spawner/random_spawners/wall_rusted_always/randspawn(turf/T) + rustify(T) + qdel(src) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c3ca91192c98..333473ed9770 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -994,3 +994,36 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons /obj/item/proc/should_stack_with(obj/item/other) return type == other.type && name == other.name + +/** + * Handles the bulk of cigarette lighting interactions. You must call `light()` to actually light the cigarette. + * + * Returns: the target's cigarette (or the cigarette itself if attacked directly) if all checks are passed. + * If the cigarette is already lit, or is a fancy smokable being lit by anything other than a zippo or match, will return `FALSE`. + * Otherwise it will return `null`. + * Arguments: + * * user - The mob trying to light the cigarette. + * * target - The mob with the cigarette. + * * direct_attackby_item - Used if the cigarette item is clicked on directly with a lighter instead of a mob wearing a cigarette. + */ +/obj/item/proc/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + if(!user || !target) + return null + + var/obj/item/clothing/mask/cigarette/cig = direct_attackby_item ? direct_attackby_item : target.wear_mask + if(!istype(cig)) + return null + + if(user.zone_selected != "mouth" || !user.a_intent == INTENT_HELP) + return null + + if(cig.lit) + to_chat(user, "[cig] is already lit!") + return FALSE + + // Only matches and cigars can light fancy smokables. + if(cig.fancy && !istype(src, /obj/item/match) && !istype(src, /obj/item/lighter/zippo)) + to_chat(user, "[cig] straight out REFUSES to be lit by such uncivilized means!") + return FALSE + + return cig diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 3a992cd935c7..8ac39ab3c0ed 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -226,6 +226,22 @@ attack_verb = list("burnt", "singed") START_PROCESSING(SSobj, src) +/obj/item/flashlight/flare/used + +/obj/item/flashlight/flare/used/Initialize() + . = ..() + // fuel gets set on New which is annoying so these can't just be vars + fuel = 0 + on = 0 + update_icon() + +/obj/item/flashlight/flare/glowstick/used/Initialize() + . = ..() + // fuel gets set on New which is annoying so these can't just be vars + fuel = 0 + on = 0 + update_icon() + /obj/item/flashlight/flare/decompile_act(obj/item/matter_decompiler/C, mob/user) if(isdrone(user) && !fuel) C.stored_comms["metal"] += 1 diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 1d56a6af5c30..642e3f497f2c 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -352,10 +352,12 @@ item_state = "com_headset_alt" /obj/item/radio/headset/ert/alt/solgov - name = "\improper Trans-Solar Federation Marine's bowman headset" + name = "\improper Trans-Solar Marine Corps bowman headset" + desc = "An ergonomic combat headset used by the TSMC. Protects against loud noises." /obj/item/radio/headset/ert/alt/solgovviper - name = "\improper Trans-Solar Federation Infiltrator's bowman headset" + name = "\improper 3rd SOD bowman headset" + desc = "A custom-fitted headset used by the commandos of the Federal Army's renowned 3rd Special Operations Detachment, more commonly known as the Vipers." /obj/item/radio/headset/ert/alt/commander name = "ERT commander's bowman headset" @@ -364,7 +366,8 @@ instant = TRUE /obj/item/radio/headset/ert/alt/commander/solgov - name = "\improper Trans-Solar Federation Lieutenant's bowman headset" + name = "\improper Trans-Solar Marine Corps officer's bowman headset" + desc = "An ergonomic combat headset used by the TSMC. This model is equipped with an extra-strength transmitter for barking orders." /obj/item/radio/headset/centcom name = "centcom officer's bowman headset" diff --git a/code/game/objects/items/dyeing.dm b/code/game/objects/items/dyeing.dm index 3c240c1e1af3..998642e96bed 100644 --- a/code/game/objects/items/dyeing.dm +++ b/code/game/objects/items/dyeing.dm @@ -22,21 +22,23 @@ var/obj/item/target_type = GLOB.dye_registry[dye_key_selector][dye_color] if(!target_type) return FALSE + var/obj/item/target_obj = new target_type // update icons - icon = initial(target_type.icon) - icon_state = initial(target_type.icon_state) - item_state = initial(target_type.item_state) - sprite_sheets = initial(target_type.sprite_sheets) + icon = initial(target_obj.icon) + icon_state = initial(target_obj.icon_state) + item_state = initial(target_obj.item_state) + sprite_sheets = target_obj.sprite_sheets // update inhand sprites - lefthand_file = initial(target_type.lefthand_file) - righthand_file = initial(target_type.righthand_file) - inhand_x_dimension = initial(target_type.inhand_x_dimension) - inhand_y_dimension = initial(target_type.inhand_y_dimension) + lefthand_file = initial(target_obj.lefthand_file) + righthand_file = initial(target_obj.righthand_file) + inhand_x_dimension = initial(target_obj.inhand_x_dimension) + inhand_y_dimension = initial(target_obj.inhand_y_dimension) // update the name/description - name = initial(target_type.name) + name = initial(target_obj.name) desc += "\nThe colors look a little dodgy." + qdel(target_obj) update_appearance(ALL) return target_type diff --git a/code/game/objects/items/robot/cyborg_gripper.dm b/code/game/objects/items/robot/cyborg_gripper.dm index fed267133598..ac58fc2fe3ce 100644 --- a/code/game/objects/items/robot/cyborg_gripper.dm +++ b/code/game/objects/items/robot/cyborg_gripper.dm @@ -324,7 +324,8 @@ /obj/item/coin, /obj/item/paper, /obj/item/photo, - /obj/item/toy/plushie + /obj/item/toy/plushie, + /obj/item/clothing/mask/cigarette ) // Mining Gripper diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 59a94e277d50..777564380bfe 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -491,7 +491,7 @@ name = "janitorial cyborg abductor upgrade" desc = "An experimental upgrade that replaces a janitorial cyborg's tools with the abductor versions." icon_state = "abductor_mod" - origin_tech = "biotech=6;materials=6;abductor=3" + origin_tech = "biotech=6;materials=6;abductor=2" require_module = TRUE module_type = /obj/item/robot_module/janitor items_to_replace = list( diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index ccf5c97e6743..72924db35c19 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -52,9 +52,14 @@ merge_type = type if(merge && !(amount >= max_amount)) - for(var/obj/item/stack/S in loc) - if(S.merge_type == merge_type) - merge(S) + for(var/obj/item/stack/item_stack in loc) + if(item_stack == src) + continue + if(item_stack.merge_type == merge_type) + INVOKE_ASYNC(src, PROC_REF(merge_without_del), item_stack) + // we do not want to qdel during initialization, so we just check whether or not we're a 0 count stack + if(is_zero_amount(FALSE)) + return INITIALIZE_HINT_QDEL update_icon(UPDATE_ICON_STATE) @@ -147,7 +152,7 @@ to_chat(user, "Your [material.name] stack now contains [material.get_amount()] [material.singular_name]\s.") /obj/item/stack/use(used, check = TRUE) - if(check && zero_amount()) + if(check && is_zero_amount(TRUE)) return FALSE if(is_cyborg) @@ -158,7 +163,7 @@ amount -= used if(check) - zero_amount() + is_zero_amount(TRUE) update_icon(UPDATE_ICON_STATE) return TRUE @@ -289,9 +294,13 @@ use(amount) SStgui.update_uis(src) -// Returns TRUE if the stack amount is zero. -// Also qdels the stack gracefully if it is. -/obj/item/stack/proc/zero_amount() +/** + * Returns TRUE if the item stack is the equivalent of a 0 amount item. + * + * Also deletes the item if delete_if_zero is TRUE and the stack does not have + * is_cyborg set to true. + */ +/obj/item/stack/proc/is_zero_amount(delete_if_zero = TRUE) if(is_cyborg) return source.amount < cost @@ -299,17 +308,27 @@ if(ismob(loc)) var/mob/living/L = loc // At this stage, stack code is so horrible and atrocious, I wouldn't be all surprised ghosts can somehow have stacks. If this happens, then the world deserves to burn. L.unEquip(src, TRUE) - if(amount < 1) - // If you stand on top of a stack, and drop a - different - 0-amount stack on the floor, - // the two get merged, so the amount of items in the stack can increase from the 0 that it had before. - // Check the amount again, to be sure we're not qdeling healthy stacks. + if(delete_if_zero) qdel(src) return TRUE return FALSE -/obj/item/stack/proc/merge(obj/item/stack/material) //Merge src into S, as much as possible - if(QDELETED(material) || QDELETED(src) || material == src) //amusingly this can cause a stack to consume itself, let's not allow that. - return FALSE +/** + * Merges as much of src into material as possible. + * + * This calls use() without check = FALSE, preventing the item from qdeling itself if it reaches 0 stack size. + * + * As a result, this proc can leave behind a 0 amount stack. + */ +/obj/item/stack/proc/merge_without_del(obj/item/stack/material) + // Cover edge cases where multiple stacks are being merged together and haven't been deleted properly. + // Also cover edge case where a stack is being merged into itself, which is supposedly possible. + if(QDELETED(material)) + CRASH("Stack merge attempted on qdeleted target stack.") + if(QDELETED(src)) + CRASH("Stack merge attempted on qdeleted source stack.") + if(material == src) + CRASH("Stack attempted to merge into itself.") var/transfer = get_amount() if(material.is_cyborg) @@ -317,18 +336,24 @@ else transfer = min(transfer, material.max_amount - material.amount) - if(transfer <= 0) - return - if(pulledby) pulledby.start_pulling(material) material.copy_evidences(src) + use(transfer, FALSE) material.add(transfer) - use(transfer) - SStgui.update_uis(material) + return transfer +/** + * Merges as much of src into material as possible. + * + * This proc deletes src if the remaining amount after the transfer is 0. + */ +/obj/item/stack/proc/merge(obj/item/stack/material) + . = merge_without_del(material) + is_zero_amount(TRUE) + /obj/item/stack/proc/copy_evidences(obj/item/stack/material) blood_DNA = material.blood_DNA fingerprints = material.fingerprints diff --git a/code/game/objects/items/tools/welder.dm b/code/game/objects/items/tools/welder.dm index 891223a9efe8..40edd403aaff 100644 --- a/code/game/objects/items/tools/welder.dm +++ b/code/game/objects/items/tools/welder.dm @@ -153,19 +153,31 @@ return remove_fuel(0.5) -/obj/item/weldingtool/attack(mob/living/carbon/M, mob/living/carbon/user) - // For lighting other people's cigarettes. - var/obj/item/clothing/mask/cigarette/cig = M?.wear_mask - if(!istype(cig) || user.zone_selected != "mouth" || !tool_enabled) +/obj/item/weldingtool/attack(mob/living/target, mob/living/user) + if(!cigarette_lighter_act(user, target)) return ..() - if(M == user) - cig.attackby(src, user) - return +/obj/item/weldingtool/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!tool_enabled) + to_chat(user, "You need to activate [src] before you can light anything with it!") + return TRUE - cig.light("[user] holds out [src] out for [M], and casually lights [cig]. What a badass.") - playsound(src, 'sound/items/lighter/light.ogg', 25, TRUE) - M.update_inv_wear_mask() + if(target == user) + user.visible_message( + "[user] casually lights [cig] with [src], what a badass.", + "You light [cig] with [src]." + ) + else + user.visible_message( + "[user] holds out [src] out for [target], and casually lights [cig]. What a badass.", + "You light [cig] for [target] with [src]." + ) + cig.light(user, target) + return TRUE /obj/item/weldingtool/use_tool(atom/target, user, delay, amount, volume, datum/callback/extra_checks) target.add_overlay(GLOB.welding_sparks) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index d96dbff1f5a4..b50ff4b90e8b 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -211,7 +211,7 @@ ..() if(istype(W, /obj/item/toy/sword)) if(W == src) - to_chat(user, "You try to attach the end of the plastic sword to... itself. You're not very smart, are you?") + to_chat(user, "You try to attach the end of the plastic sword to... Itself. You're not very smart, are you?") if(ishuman(user)) user.adjustBrainLoss(10) else if((W.flags & NODROP) || (flags & NODROP)) @@ -229,10 +229,64 @@ /// Sets to TRUE once the character using it hits something and realises it's not a real energy sword var/pranked = FALSE +/obj/item/toy/sword/attack(mob/target, mob/living/user) + if(!cigarette_lighter_act(user, target)) + return ..() + +/obj/item/toy/sword/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!active) + to_chat(user, "You must activate [src] before you can light [cig] with it!") + return TRUE + + user.do_attack_animation(target) + + // 1% chance to light the cig. Somehow... + if(prob(1)) + if(target == user) + user.visible_message( + "[user] makes a violent slashing motion, barely missing [user.p_their()] nose as light flashes! \ + [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [cig] with [src] in the process. Somehow...", + "You casually slash [src] at [cig], lighting it with the blade. Somehow...", + "You hear an energy blade slashing something!" + ) + else + user.visible_message( + "[user] makes a violent slashing motion, barely missing the nose of [target] as light flashes! \ + [user.p_they(TRUE)] light[user.p_s()] [cig] in the mouth of [target] with [src] in the process. Somehow...", + "You casually slash [src] at [cig] in the mouth of [target], lighting it with the blade. Somehow...", + "You hear an energy blade slashing something!" + ) + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, TRUE) + cig.light(user, target) + return TRUE + + // Else, bat it out of the target's mouth. + if(target == user) + user.visible_message( + "[user] makes a violent slashing motion, barely missing [user.p_their()] nose as light flashes! \ + [user.p_they(TRUE)] instead hit [cig], knocking it out of [user.p_their()] mouth and dropping it to the floor.", + "You casually slash [src] at [cig], swatting it out of your mouth.", + "You hear a gentle tapping." + ) + else + user.visible_message( + "[user] makes a violent slashing motion, barely missing the nose of [target] as light flashes! \ + [user] does hit [cig], knocking it out of the mouth of [target] and dropping it to the floor. Wow, rude!", + "You casually slash [src] at [cig] in the mouth of [target], swatting it to the floor!", + "You hear a gentle tapping." + ) + playsound(loc, 'sound/weapons/tap.ogg', vary = TRUE) + target.unEquip(cig, TRUE) + return TRUE + /obj/item/toy/sword/chaosprank/afterattack(mob/living/target, mob/living/user, proximity) ..() if(!pranked) - to_chat(user, "Oh... it's a fake.") + to_chat(user, "Oh... It's a fake.") name = "toy sword" pranked = TRUE @@ -241,7 +295,7 @@ */ /obj/item/dualsaber/toy name = "double-bladed toy sword" - desc = "A cheap, plastic replica of TWO energy swords. Double the fun!" + desc = "A cheap, plastic replica of TWO energy swords. Double the fun!" force = 0 throwforce = 0 throw_speed = 3 @@ -280,7 +334,10 @@ hitsound = 'sound/weapons/bladeslice.ogg' /obj/item/toy/katana/suicide_act(mob/user) - var/dmsg = pick("[user] tries to stab \the [src] into [user.p_their()] abdomen, but it shatters! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.","[user] tries to stab \the [src] into [user.p_their()] abdomen, but \the [src] bends and breaks in half! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.","[user] tries to slice [user.p_their()] own throat, but the plastic blade has no sharpness, causing [user.p_them()] to lose [user.p_their()] balance, slip over, and break [user.p_their()] neck with a loud snap!") + var/dmsg = pick( + "[user] tries to stab [src] into [user.p_their()] abdomen, but it shatters! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.", + "[user] tries to stab [src] into [user.p_their()] abdomen, but [src] bends and breaks in half! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.", + "[user] tries to slice [user.p_their()] own throat, but the plastic blade has no sharpness, causing [user.p_them()] to lose [user.p_their()] balance, slip over, and break [user.p_their()] neck with a loud snap!") user.visible_message("[dmsg] It looks like [user.p_theyre()] trying to commit suicide!") return BRUTELOSS @@ -1051,6 +1108,10 @@ attack_verb = list("attacked", "bashed", "smashed", "stoned") hitsound = "swing_hit" +/obj/item/toy/pet_rock/attack_self(mob/user) + var/cuddle_verb = pick("admires", "respects", "cherises", "appreciates") + user.visible_message("[user] [cuddle_verb] [src].") + /obj/item/toy/pet_rock/fred name = "fred" desc = "Fred, the bestest boy pet in the whole wide universe!" diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 113111f1929d..642d9bf6753c 100755 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -134,7 +134,7 @@ AI MODULES ..() var/law = "Safeguard [targetName]. Individuals that threaten [targetName] are not crew and must be eliminated.'" to_chat(target, law) - target.add_supplied_law(4, law) + target.add_inherent_law(law) GLOB.lawchanges.Add("The law specified [targetName]") /******************** oneCrewMember ********************/ @@ -185,7 +185,7 @@ AI MODULES ..() var/law = "Protect the space station against damage. Anyone you see harming the station is to be no longer considered crew, and is a threat to the station which must be neutralized." to_chat(target, law) - target.add_supplied_law(5, law) + target.add_inherent_law(law) /******************** OxygenIsToxicToCrew ********************/ /obj/item/aiModule/oxygen diff --git a/code/game/objects/items/weapons/cigs.dm b/code/game/objects/items/weapons/cigs.dm index 085c66cbab3a..5680009ec55a 100644 --- a/code/game/objects/items/weapons/cigs.dm +++ b/code/game/objects/items/weapons/cigs.dm @@ -1,11 +1,12 @@ #define REAGENT_TIME_RATIO 2.5 /* -CONTAINS: -CIGARETTES -CIGARS -SMOKING PIPES -HOLO-CIGAR +CONTENTS: +1. CIGARETTES +2. CIGARS +3. HOLO-CIGAR +4. PIPES +5. ROLLING CIGARETTE PACKETS ARE IN FANCY.DM LIGHTERS ARE IN LIGHTERS.DM @@ -19,22 +20,31 @@ LIGHTERS ARE IN LIGHTERS.DM name = "cigarette" desc = "A roll of tobacco and nicotine." icon_state = "cigoff" - throw_speed = 0.5 item_state = "cigoff" + throw_speed = 0.5 slot_flags = SLOT_FLAG_MASK w_class = WEIGHT_CLASS_TINY body_parts_covered = null attack_verb = null container_type = INJECTABLE + /// Is the cigarette lit? var/lit = FALSE + /// Lit cigarette sprite. var/icon_on = "cigon" //Note - these are in masks.dmi not in cigarette.dmi + /// Unlit cigarette sprite. var/icon_off = "cigoff" + /// Are we an extra-classy smokable? + var/fancy = FALSE + /// What trash item the cigarette makes when it burns out. var/type_butt = /obj/item/cigbutt - var/lastHolder = null - var/smoketime = 150 + /// How long does the cigarette last before going out? Decrements by 1 every cycle. + var/smoketime = 150 // 300 seconds. + /// The cigarette's total reagent capacity. var/chem_volume = 60 + /// A list of the types and amounts of reagents in the cigarette. var/list/list_reagents = list("nicotine" = 40) - var/first_puff = TRUE // the first puff is a bit more reagents ingested + /// Has anyone taken any reagents from the cigarette? The first tick gives a bigger dose. + var/first_puff = TRUE sprite_sheets = list( "Vox" = 'icons/mob/clothing/species/vox/mask.dmi', "Unathi" = 'icons/mob/clothing/species/unathi/mask.dmi', @@ -42,14 +52,6 @@ LIGHTERS ARE IN LIGHTERS.DM "Vulpkanin" = 'icons/mob/clothing/species/vulpkanin/mask.dmi', "Grey" = 'icons/mob/clothing/species/grey/mask.dmi') - var/static/things_that_light = typecacheof(list( - /obj/item/lighter, - /obj/item/match, - /obj/item/melee/energy/sword/saber, - /obj/item/assembly/igniter, - /obj/item/gun/magic/wand/fireball)) - - /obj/item/clothing/mask/cigarette/Initialize(mapload) . = ..() create_reagents(chem_volume) // making the cigarrete a chemical holder with a maximum volume of 30 @@ -57,19 +59,12 @@ LIGHTERS ARE IN LIGHTERS.DM if(list_reagents) reagents.add_reagent_list(list_reagents) smoketime = reagents.total_volume * 2.5 - RegisterSignal(src, COMSIG_ITEM_BEING_ATTACKED, PROC_REF(try_light)) /obj/item/clothing/mask/cigarette/Destroy() QDEL_NULL(reagents) STOP_PROCESSING(SSobj, src) return ..() -/obj/item/clothing/mask/cigarette/proc/try_light(obj/item/cigarette, obj/item/lighting_item) - SIGNAL_HANDLER - if(lighting_item.get_heat()) - light() - return COMPONENT_CANCEL_ATTACK_CHAIN - /obj/item/clothing/mask/cigarette/decompile_act(obj/item/matter_decompiler/C, mob/user) if(isdrone(user)) C.stored_comms["wood"] += 1 @@ -81,17 +76,74 @@ LIGHTERS ARE IN LIGHTERS.DM if(istype(M) && M.on_fire) user.changeNext_move(CLICK_CD_MELEE) user.do_attack_animation(M) - light("[user] coldly lights [src] with the burning body of [M]. Clearly, [user.p_they()] offer[user.p_s()] the warmest of regards...") + if(M != user) + user.visible_message( + "[user] coldly lights [src] with the burning body of [M]. Clearly, [user.p_they()] offer[user.p_s()] the warmest of regards...", + "You coldly light [src] with the burning body of [M]." + ) + else + // The fire will light it in your hands by itself, but if you whip out the cig and click yourself fast enough, this will happen. TRULY you have your priorities stright. + user.visible_message( + "[user] quickly whips out [src] and nonchalantly lights it with [user.p_their()] own burning body. Clearly, [user.p_they()] [user.p_have()] [user.p_their()] priorities straight.", + "You quickly whip out [src] and nonchalantly light it with your own burning body. Clearly, you have your priorities straight." + ) + light(user, user) return TRUE - else - return ..() + +/obj/item/clothing/mask/cigarette/afterattack(atom/target, mob/living/user, proximity) + if(!proximity) + return + + if(ismob(target)) + // If the target has no cig, try to give them the cig. + var/mob/living/carbon/M = target + if(istype(M) && user.zone_selected == "mouth" && !M.wear_mask && user.a_intent == INTENT_HELP) + user.unEquip(src, TRUE) + M.equip_to_slot_if_possible(src, SLOT_HUD_WEAR_MASK) + if(target != user) + user.visible_message( + "[user] slips \a [name] into the mouth of [M].", + "You slip [src] into the mouth of [M]." + ) + else + to_chat(user, "You put [src] into your mouth.") + return TRUE + + // If they DO have a cig, try to light it with your own cig. + if(!cigarette_lighter_act(user, M)) + return ..() + + // You can dip cigarettes into beakers. + if(istype(target, /obj/item/reagent_containers/glass)) + var/obj/item/reagent_containers/glass/glass = target + var/transfered = glass.reagents.trans_to(src, chem_volume) + if(transfered) + to_chat(user, "You dip [src] into [glass].") + return + + // Either the beaker was empty, or the cigarette was full + if(!glass.reagents.total_volume) + to_chat(user, "[glass] is empty.") + else + to_chat(user, "[src] is full.") + + return ..() + +/obj/item/clothing/mask/cigarette/attack_self(mob/user) + if(lit) + user.visible_message( + "[user] calmly drops and treads on [src], putting it out instantly.", + "You calmly drop and tread on [src], putting it out instantly.", + "You hear a foot being brought down on something, and the tiny fizzling of an ember going out." + ) + die() + return ..() /obj/item/clothing/mask/cigarette/can_enter_storage(obj/item/storage/S, mob/user) if(lit) - to_chat(user, "[S] can't hold [initial(name)] while it's lit!") // initial(name) so it doesn't say "lit" twice in a row + to_chat(user, "[S] can't hold \the [initial(name)] while it's lit!") // initial(name) so it doesn't say "lit" twice in a row return FALSE - else - return TRUE + return TRUE /obj/item/clothing/mask/cigarette/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) ..() @@ -101,116 +153,95 @@ LIGHTERS ARE IN LIGHTERS.DM if(!lit) light("[src] is lit by the flames!") -/obj/item/clothing/mask/cigarette/welder_act(mob/user, obj/item/I) - . = TRUE - if(I.tool_use_check(user, 0)) //Don't need to flash eyes because you are a badass - light("[user] casually lights [src] with [I], what a badass.") +/obj/item/clothing/mask/cigarette/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) -/obj/item/clothing/mask/cigarette/attackby(obj/item/I, mob/user, params) - ..() - if(istype(I, /obj/item/lighter/zippo)) - var/obj/item/lighter/zippo/Z = I - if(Z.lit) - light("With a single flick of [user.p_their()] wrist, [user] smoothly lights [user.p_their()] [name] with [user.p_their()] [Z]. Damn [user.p_theyre()] cool.") - - else if(istype(I, /obj/item/lighter)) - var/obj/item/lighter/L = I - if(L.lit) - light("After some fiddling, [user] manages to light [user.p_their()] [name] with [L].") - - else if(istype(I, /obj/item/match/unathi)) - var/obj/item/match/unathi/U = I - if(U.lit) - playsound(user.loc, 'sound/effects/unathiignite.ogg', 40, FALSE) - light("[user] spits fire at [user.p_their()] [name], igniting it.") - U.matchburnout() - - else if(istype(I, /obj/item/match)) - var/obj/item/match/M = I - if(M.lit) - light("[user] lights [user.p_their()] [name] with [user.p_their()] [M].") - - else if(istype(I, /obj/item/melee/energy/sword/saber)) - var/obj/item/melee/energy/sword/saber/S = I - if(HAS_TRAIT(S, TRAIT_ITEM_ACTIVE)) - light("[user] makes a violent slashing motion, barely missing [user.p_their()] nose as light flashes. [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [name] with [S] in the process.") - - else if(istype(I, /obj/item/assembly/igniter)) - light("[user] fiddles with [I], and manages to light [user.p_their()] [name].") - - else if(istype(I, /obj/item/gun/magic/wand/fireball)) - var/obj/item/gun/magic/wand/fireball/F = I - if(F.charges) - if(prob(50) || user.mind.assigned_role == "Wizard") - light("Holy shit, did [user] just manage to light [user.p_their()] [name] with [F], with only moderate eyebrow singing?") - else - to_chat(user, "Unsure which end of the wand is which, [user] fails to light [name] with [F].") - explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) - F.charges-- + if(!lit) + to_chat(user, "You cannot light [cig] with [src] because you need a lighter to light [src] before you can use [src] as a lighter to light [cig]... This seems a little convoluted.") + return TRUE - //can't think of any other way to update the overlays :< - user.update_inv_wear_mask() - user.update_inv_l_hand() - user.update_inv_r_hand() + if(target == user) + user.visible_message( + "[user] presses [src] against [cig] until it lights. Seems oddly recursive...", + "You press [src] against [cig] until it lights. Seems oddly recursive..." + ) + else + user.visible_message( + "[user] presses [src] until it lights. Sharing is caring!", + "You press [src] against [cig] until it lights. Sharing is caring!" + ) + cig.light(user, target) + return TRUE + +/obj/item/clothing/mask/cigarette/attackby(obj/item/I, mob/living/user, params) + if(I.cigarette_lighter_act(user, user, src)) + return + // Catch any item that has no cigarette_lighter_act but logically should be able to work as a lighter due to being hot. + if(I.get_heat()) + //Give a generic light message. + user.visible_message( + "[user] lights [src] with [I]", + "You light [src] with [I]." + ) + light(user) -/obj/item/clothing/mask/cigarette/afterattack(obj/item/reagent_containers/glass/glass, mob/user, proximity) - ..() - if(!proximity) +/obj/item/clothing/mask/cigarette/proc/light(mob/living/user, mob/living/target) + if(lit) return - if(istype(glass)) //you can dip cigarettes into beakers - var/transfered = glass.reagents.trans_to(src, chem_volume) - if(transfered) //if reagents were transfered, show the message - smoketime = reagents.total_volume * 2.5 - to_chat(user, "You dip \the [src] into \the [glass].") - else //if not, either the beaker was empty, or the cigarette was full - if(!glass.reagents.total_volume) - to_chat(user, "[glass] is empty.") - else - to_chat(user, "[src] is full.") + lit = TRUE + name = "lit [name]" + attack_verb = list("burnt", "singed") + hitsound = 'sound/items/welder.ogg' + damtype = BURN + force = 4 + var/mob/M = loc + + // Plasma explodes when exposed to fire. + if(reagents.get_reagent_amount("plasma")) + var/datum/effect_system/reagents_explosion/e = new() + e.set_up(round(reagents.get_reagent_amount("plasma") / 2.5, 1), get_turf(src), 0, 0) + e.start() + if(ismob(M)) + M.unEquip(src, TRUE) + qdel(src) + return -/obj/item/clothing/mask/cigarette/proc/light(flavor_text = null) - if(!lit) - lit = TRUE - name = "lit [name]" - attack_verb = list("burnt", "singed") - hitsound = 'sound/items/welder.ogg' - damtype = "fire" - force = 4 - if(reagents.get_reagent_amount("plasma")) // the plasma explodes when exposed to fire - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount("plasma") / 2.5, 1), get_turf(src), 0, 0) - e.start() - if(ismob(loc)) - var/mob/M = loc - M.unEquip(src, 1) - qdel(src) - return - if(reagents.get_reagent_amount("fuel")) // the fuel explodes, too, but much less violently - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount("fuel") / 5, 1), get_turf(src), 0, 0) - e.start() - if(ismob(loc)) - var/mob/M = loc - M.unEquip(src, 1) - qdel(src) - return - reagents.set_reacting(TRUE) - reagents.handle_reactions() - icon_state = icon_on - item_state = icon_on - if(flavor_text) - var/turf/T = get_turf(src) - T.visible_message(flavor_text) - if(iscarbon(loc)) - var/mob/living/carbon/C = loc - if(C.wear_mask == src) // Don't update if it's just in their hand - C.wear_mask_update(src) - set_light(2, 0.25, "#E38F46") - START_PROCESSING(SSobj, src) - playsound(src, 'sound/items/lighter/light.ogg', 25, TRUE) + // Fuel explodes, too, but much less violently. + if(reagents.get_reagent_amount("fuel")) + var/datum/effect_system/reagents_explosion/e = new() + e.set_up(round(reagents.get_reagent_amount("fuel") / 5, 1), get_turf(src), 0, 0) + e.start() + if(ismob(M)) + M.unEquip(src, TRUE) + qdel(src) + return + + // If there is no target, the user is probably lighting their own cig. + if(isnull(target)) + target = user + // If there is also no user, the cig is being lit by atmos or something. + if(target) + target.update_inv_wear_mask() + target.update_inv_l_hand() + target.update_inv_r_hand() + + reagents.set_reacting(TRUE) + reagents.handle_reactions() + icon_state = icon_on + item_state = icon_on + if(iscarbon(loc)) + var/mob/living/carbon/C = loc + if(C.wear_mask == src) // Don't update if it's just in their hand + C.wear_mask_update(src) + set_light(2, 0.25, "#E38F46") + START_PROCESSING(SSobj, src) + playsound(src, 'sound/items/lighter/light.ogg', 25, TRUE) + return TRUE /obj/item/clothing/mask/cigarette/process() var/mob/living/M = loc @@ -222,18 +253,11 @@ LIGHTERS ARE IN LIGHTERS.DM return smoke() - /obj/item/clothing/mask/cigarette/extinguish_light(force) if(!force) return die() -/obj/item/clothing/mask/cigarette/attack_self(mob/user) - if(lit) - user.visible_message("[user] calmly drops and treads on [src], putting it out instantly.") - die() - return ..() - /obj/item/clothing/mask/cigarette/proc/smoke() var/turf/location = get_turf(src) var/is_being_smoked = FALSE @@ -264,16 +288,16 @@ LIGHTERS ARE IN LIGHTERS.DM if(ismob(loc)) var/mob/living/M = loc to_chat(M, "Your [name] goes out.") - M.unEquip(src, 1) //Force the un-equip so the overlays update + M.unEquip(src, TRUE) //Force the un-equip so the overlays update STOP_PROCESSING(SSobj, src) qdel(src) /obj/item/clothing/mask/cigarette/get_heat() return lit * 1000 -/obj/item/clothing/mask/cigarette/proc/can_light_fancy(obj/item/lighting_item) - return (istype(lighting_item, /obj/item/match) || istype(lighting_item, /obj/item/lighter/zippo)) - +////////////////////////////// +// MARK: CIGARETTES +////////////////////////////// /obj/item/clothing/mask/cigarette/menthol list_reagents = list("nicotine" = 40, "menthol" = 20) @@ -314,6 +338,26 @@ LIGHTERS ARE IN LIGHTERS.DM /obj/item/clothing/mask/cigarette/rollie/custom list_reagents = list() +/obj/item/cigbutt + name = "cigarette butt" + desc = "A manky old cigarette butt." + icon = 'icons/obj/clothing/masks.dmi' + icon_state = "cigbutt" + w_class = WEIGHT_CLASS_TINY + throwforce = 1 + +/obj/item/cigbutt/Initialize(mapload) + . = ..() + pixel_x = rand(-10, 10) + pixel_y = rand(-10, 10) + transform = turn(transform, rand(0, 360)) + +/obj/item/cigbutt/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(isdrone(user)) + C.stored_comms["wood"] += 1 + qdel(src) + return TRUE + return ..() /obj/item/cigbutt/roach name = "roach" @@ -325,27 +369,59 @@ LIGHTERS ARE IN LIGHTERS.DM pixel_x = rand(-5, 5) pixel_y = rand(-5, 5) -//////////// -// CIGARS // -//////////// +////////////////////////////// +// MARK: ROLLING +////////////////////////////// +/obj/item/rollingpaper + name = "rolling paper" + desc = "A thin piece of paper used to make fine smokeables." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "cig_paper" + w_class = WEIGHT_CLASS_TINY + +/obj/item/rollingpaper/afterattack(atom/target, mob/user, proximity) + if(!proximity) + return + + if(!istype(target, /obj/item/food/grown)) + return ..() + + var/obj/item/food/grown/plant = target + if(!plant.dry) + to_chat(user, "You need to dry this first!") + return + user.unEquip(plant, TRUE) + user.unEquip(src, TRUE) + var/obj/item/clothing/mask/cigarette/rollie/custom/custom_rollie = new (get_turf(user)) + custom_rollie.reagents.maximum_volume = plant.reagents.total_volume + plant.reagents.trans_to(custom_rollie, plant.reagents.total_volume) + custom_rollie.smoketime = custom_rollie.reagents.total_volume * 2.5 + + user.put_in_active_hand(custom_rollie) + to_chat(user, "You roll the [plant.name] into a rolling paper.") + custom_rollie.desc = "Dried [plant.name] rolled up in a thin piece of paper." + + qdel(plant) + qdel(src) + +////////////////////////////// +// MARK: CIGARS +////////////////////////////// /obj/item/clothing/mask/cigarette/cigar name = "\improper Premium Cigar" desc = "A brown roll of tobacco and... well, you're not quite sure. This thing's huge!" icon_state = "cigaroff" + item_state = "cigaroff" icon_on = "cigaron" icon_off = "cigaroff" - type_butt = /obj/item/cigbutt/cigarbutt throw_speed = 0.5 - item_state = "cigaroff" + fancy = TRUE + type_butt = /obj/item/cigbutt/cigarbutt smoketime = 300 chem_volume = 120 list_reagents = list("nicotine" = 120) -/obj/item/clothing/mask/cigarette/cigar/try_light(obj/item/cigar, obj/item/lighting_item) - if(can_light_fancy(lighting_item)) - return ..() - /obj/item/clothing/mask/cigarette/cigar/cohiba name = "\improper Cohiba Robusto Cigar" desc = "There's little more you could want from a cigar." @@ -363,45 +439,19 @@ LIGHTERS ARE IN LIGHTERS.DM chem_volume = 180 list_reagents = list("nicotine" = 180) -/obj/item/cigbutt - name = "cigarette butt" - desc = "A manky old cigarette butt." - icon = 'icons/obj/clothing/masks.dmi' - icon_state = "cigbutt" - w_class = WEIGHT_CLASS_TINY - throwforce = 1 - -/obj/item/cigbutt/Initialize(mapload) - . = ..() - pixel_x = rand(-10,10) - pixel_y = rand(-10,10) - transform = turn(transform,rand(0,360)) - -/obj/item/cigbutt/decompile_act(obj/item/matter_decompiler/C, mob/user) - if(isdrone(user)) - C.stored_comms["wood"] += 1 - qdel(src) - return TRUE - return ..() - /obj/item/cigbutt/cigarbutt name = "cigar butt" desc = "A manky old cigar butt." icon_state = "cigarbutt" - -/obj/item/clothing/mask/cigarette/cigar/attackby(obj/item/I, mob/user, params) - if(!is_type_in_typecache(I, things_that_light)) - return - if(can_light_fancy(I)) - ..() - else - to_chat(user, "[src] straight out REFUSES to be lit by such uncivilized means.") - +////////////////////////////// +// MARK: HOLO-CIGAR +////////////////////////////// /obj/item/clothing/mask/holo_cigar name = "Holo-Cigar" desc = "A sleek electronic cigar imported straight from Sol. You feel badass merely glimpsing it..." icon_state = "holocigaroff" + /// Is the holo-cigar lit? var/enabled = FALSE /// Tracks if this is the first cycle smoking the cigar. var/has_smoked = FALSE @@ -462,10 +512,9 @@ LIGHTERS ARE IN LIGHTERS.DM update_appearance(UPDATE_ICON_STATE) -///////////////// -//SMOKING PIPES// -///////////////// - +////////////////////////////// +// MARK: PIPES +////////////////////////////// /obj/item/clothing/mask/cigarette/pipe name = "smoking pipe" desc = "A pipe, for smoking. Probably made of meershaum or something." @@ -473,6 +522,7 @@ LIGHTERS ARE IN LIGHTERS.DM item_state = "pipeoff" icon_on = "pipeon" //Note - these are in masks.dmi icon_off = "pipeoff" + fancy = TRUE smoketime = 500 chem_volume = 200 list_reagents = list("nicotine" = 200) @@ -480,15 +530,12 @@ LIGHTERS ARE IN LIGHTERS.DM /obj/item/clothing/mask/cigarette/pipe/die() return -/obj/item/clothing/mask/cigarette/pipe/light(flavor_text = null) +/obj/item/clothing/mask/cigarette/pipe/light() if(!lit) lit = TRUE damtype = "fire" icon_state = icon_on item_state = icon_on - if(flavor_text) - var/turf/T = get_turf(src) - T.visible_message(flavor_text) START_PROCESSING(SSobj, src) /obj/item/clothing/mask/cigarette/pipe/process() @@ -509,18 +556,18 @@ LIGHTERS ARE IN LIGHTERS.DM /obj/item/clothing/mask/cigarette/pipe/attack_self(mob/user) // Extinguishes the pipe. if(lit) - user.visible_message("[user] puts out [src].") + user.visible_message( + "[user] puts out [src].", + "You put out [src]." + ) lit = FALSE + first_puff = TRUE icon_state = icon_off item_state = icon_off STOP_PROCESSING(SSobj, src) return -/obj/item/clothing/mask/cigarette/pipe/try_light(obj/item/cigar, obj/item/lighting_item) - if(can_light_fancy(lighting_item)) - return ..() - -// Refill or light the pipe +// Refill the pipe /obj/item/clothing/mask/cigarette/pipe/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/food/grown)) var/obj/item/food/grown/O = I @@ -534,14 +581,6 @@ LIGHTERS ARE IN LIGHTERS.DM qdel(O) else to_chat(user, "You need to dry this first!") - return - - if(!is_type_in_typecache(I, things_that_light)) - return - if(can_light_fancy(I)) - ..() - else - to_chat(user, "[src] straight out REFUSES to be lit by such means.") /obj/item/clothing/mask/cigarette/pipe/cobpipe name = "corn cob pipe" @@ -554,40 +593,4 @@ LIGHTERS ARE IN LIGHTERS.DM chem_volume = 160 list_reagents = list() -/////////// -//ROLLING// -/////////// - -/obj/item/rollingpaper - name = "rolling paper" - desc = "A thin piece of paper used to make fine smokeables." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "cig_paper" - w_class = WEIGHT_CLASS_TINY - -/obj/item/rollingpaper/afterattack(atom/target, mob/user, proximity) - if(!proximity) - return - if(!istype(target, /obj/item/food/grown)) - return ..() - - var/obj/item/food/grown/plant = target - if(!plant.dry) - to_chat(user, "You need to dry this first!") - return - - user.unEquip(plant, TRUE) - user.unEquip(src, TRUE) - var/obj/item/clothing/mask/cigarette/rollie/custom/custom_rollie = new (get_turf(user)) - custom_rollie.reagents.maximum_volume = plant.reagents.total_volume - plant.reagents.trans_to(custom_rollie, plant.reagents.total_volume) - custom_rollie.smoketime = custom_rollie.reagents.total_volume * 2.5 - - user.put_in_active_hand(custom_rollie) - to_chat(user, "You roll the [plant.name] into a rolling paper.") - custom_rollie.desc = "Dried [plant.name] rolled up in a thin piece of paper." - - qdel(plant) - qdel(src) - #undef REAGENT_TIME_RATIO diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 7940fb082c23..f496e5fdb3d7 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -74,6 +74,53 @@ else return TRUE +/obj/item/flamethrower/attack(mob/living/target, mob/living/user) + if(!cigarette_lighter_act(user, target)) + return ..() + +/obj/item/flamethrower/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!lit) + to_chat(user, "You need to ignite [src] before you can use it as a lighter!") + return TRUE + + // Pulling this off 'safely' requires years of experience, a true badass, or blind luck! + if(HAS_TRAIT(user, TRAIT_BADASS) || (user.mind.assigned_role in list("Station Engineer", "Chief Engineer", "Life Support Specialist")) || prob(50)) + if(user == target) + user.visible_message( + "[user] confidently lifts up [src] and releases a big puff of flame at [user.p_their()] [cig] to light it, like some kind of psychopath!", + "You lift up [src] and lightly pull the trigger, lighting [cig].", + "You hear a brief burst of flame!" + ) + else + user.visible_message( + "[user] confidently lifts up [src] and releases a big puff of flame at [target], lighting [target.p_their()] [cig.name], like some kind of psychopath!", + "You lift up [src] and point it at [target], lightly pullling the trigger to light [target.p_their()] [cig.name] with a big puff of flame.", + "You hear a brief burst of flame!" + ) + else + // You set them on fire, but at least the cigarette got lit... + if(target == user) + user.visible_message( + "[user] carelessly lifts up [src] and releases a large burst of flame at [user.p_their()] [cig] to light it, accidentally setting [user.p_themselves()] ablaze in the process!", + "You lift up [src] and squeeze the trigger to light [cig]. Unfortunately, you squeeze a little too hard and release a large burst of flame that sets you ablaze!", + "You hear a plume of fire and something igniting!" + ) + else + user.visible_message( + "[user] carelessly lifts up [src] and releases a large burst of flame at [target] to light [target.p_their()] [cig.name], accidentally setting [target.p_them()] ablaze!", + "You lift up [src] up and point it at [target], squeezing the trigger to light [target.p_their()] [cig.name]. \ + Unfortunately, your squeeze a little too hard and release large burst of flame that sets [target.p_them()] ablaze!", + "You hear a plume of fire and something igniting!" + ) + target.adjust_fire_stacks(2) + target.IgniteMob() + cig.light(user, target) + return TRUE + /obj/item/flamethrower/afterattack(atom/target, mob/user, flag) . = ..() if(flag) diff --git a/code/game/objects/items/weapons/lighters.dm b/code/game/objects/items/weapons/lighters.dm index 9002eeac82ed..2992f2473a88 100644 --- a/code/game/objects/items/weapons/lighters.dm +++ b/code/game/objects/items/weapons/lighters.dm @@ -1,7 +1,7 @@ -// Basic lighters +// MARK: LIGHTERS /obj/item/lighter name = "cheap lighter" - desc = "A cheap-as-free lighter." + desc = "A cheap cigarette lighter. It gets the job done, barely." icon = 'icons/obj/lighter.dmi' lefthand_file = 'icons/mob/inhands/lighter_lefthand.dmi' righthand_file = 'icons/mob/inhands/lighter_righthand.dmi' @@ -20,6 +20,7 @@ var/next_off_message /// Our lighter color suffix. => [base_icon_state]-[lightercolor] => lighter-r var/lighter_color + var/is_a_zippo = FALSE /obj/item/lighter/random base_icon_state = "lighter" @@ -95,26 +96,41 @@ playsound(src, 'sound/items/lighter/plastic_close.ogg', 25, TRUE) next_off_message = world.time + 5 SECONDS -/obj/item/lighter/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!isliving(M)) - return - M.IgniteMob() - if(!ismob(M)) +/obj/item/lighter/attack(mob/living/target, mob/living/user) + if(cigarette_lighter_act(user, target)) return - if(istype(M.wear_mask, /obj/item/clothing/mask/cigarette) && user.zone_selected == "mouth" && lit) - var/obj/item/clothing/mask/cigarette/cig = M.wear_mask - if(M == user) - cig.attackby(src, user) - else - if(istype(src, /obj/item/lighter/zippo)) - cig.light("[user] whips [src] out and holds it for [M]. [user.p_their(TRUE)] arm is as steady as the unflickering flame [user.p_they()] light[user.p_s()] \the [cig] with.") - else - cig.light("[user] holds [src] out for [M], and lights [cig].") - playsound(src, 'sound/items/lighter/light.ogg', 25, TRUE) - M.update_inv_wear_mask() + if(lit && target.IgniteMob()) + message_admins("[key_name_admin(user)] set [key_name_admin(target)] on fire") + log_game("[key_name(user)] set [key_name(target)] on fire") + + return ..() + +/obj/item/lighter/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + // Otherwise the later parts of this proc can be passed to the zippo and cause a runtime. + if(is_a_zippo) + return cig + + if(!cig) + return !isnull(cig) + + if(!lit) + to_chat(user, "You need to light [src] before it can be used to light anything!") + return TRUE + + if(target == user) + user.visible_message( + "After some fiddling, [user] manages to light [user.p_their()] [cig] with [src].", + "After some fiddling, you manage to light [cig] with [src].," + ) else - ..() + user.visible_message( + "After some fiddling, [user] manages to light [cig] for [target] with [src].", + "After some fiddling, you manage to light [cig] for [target] with [src]." + ) + cig.light(user, target) + return TRUE /obj/item/lighter/process() var/turf/location = get_turf(src) @@ -131,18 +147,24 @@ /obj/item/lighter/get_heat() return lit * 1500 -// Zippo lighters +// ZIPPO LIGHTERS + /obj/item/lighter/zippo name = "zippo lighter" - desc = "The zippo." + desc = "A premium cigarette lighter, for cool and distinguished individuals." icon_state = "zippo" item_state = "zippo" + is_a_zippo = TRUE /obj/item/lighter/zippo/turn_on_lighter(mob/living/user) . = ..() if(world.time > next_on_message) - user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.") - playsound(src.loc, 'sound/items/zippolight.ogg', 25, 1) + user.visible_message( + "Without even breaking stride, [user] flips open and lights [src] in one smooth movement.", + "Without breaking your stride, you flip open and light [src] in one smooth movement.", + "You hear a zippo being lit." + ) + playsound(src.loc, 'sound/items/zippolight.ogg', 25, TRUE) next_on_message = world.time + 5 SECONDS else to_chat(user, "You light [src].") @@ -153,11 +175,37 @@ return if(world.time > next_off_message) - user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what [user.p_theyre()] doing. Wow.") - playsound(src.loc, 'sound/items/zippoclose.ogg', 25, 1) + user.visible_message( + "You hear a quiet click as [user] shuts off [src] without even looking at what [user.p_theyre()] doing. Wow.", + "You shut off [src] without even looking at what you're doing.", + "You hear a quiet click as a zippo lighter is shut off. Wow." + ) + playsound(loc, 'sound/items/zippoclose.ogg', 25, TRUE) next_off_message = world.time + 5 SECONDS else - to_chat(user, "You shut off [src].") + to_chat(user, "You shut off [src].") + +/obj/item/lighter/zippo/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!lit) + to_chat(user, "You need to light [src] before it can be used to light anything!") + return TRUE + + if(target == user) + user.visible_message( + "With a single flick of [user.p_their()] wrist, [user] smoothly lights [user.p_their()] [cig.name] with [src]. Damn [user.p_theyre()] cool.", + "With a single flick of your wrist, you smoothly light [cig] with [src]." + ) + else + user.visible_message( + "[user] whips [src] out and holds it for [target]. [user.p_their(TRUE)] arm is as steady as the unflickering flame [user.p_they()] light [cig] with. Damn [user.p_theyre()] cool.", + "You whip [src] out and hold it for [target]. Your arm is as steady as the unflickering flame you light [cig] with." + ) + cig.light(user, target) + return TRUE /obj/item/lighter/zippo/show_off_message(mob/living/user) return @@ -165,10 +213,9 @@ /obj/item/lighter/zippo/attempt_light(mob/living/user) return -//EXTRA LIGHTERS /obj/item/lighter/zippo/nt_rep name = "gold engraved zippo" - desc = "An engraved golden Zippo lighter with the letters NT on it." + desc = "An engraved golden Zippo lighter with the letters \"NT\" engraved on the sides." icon_state = "zippo-nt" item_state = "zippo-gold" @@ -195,9 +242,8 @@ icon_state = "zippo-gonzo" item_state = "zippo-red" -/////////// -//MATCHES// -/////////// +// MARK: MATCHES + /obj/item/match name = "match" desc = "A simple match stick, used for lighting fine smokables." @@ -209,6 +255,7 @@ w_class = WEIGHT_CLASS_TINY origin_tech = "materials=1" attack_verb = null + var/is_unathi_fire = FALSE /obj/item/match/process() var/turf/location = get_turf(src) @@ -268,35 +315,43 @@ else return TRUE -/obj/item/match/attack(mob/living/carbon/M, mob/living/carbon/user) - if(!isliving(M)) - return ..() - if(lit && M.IgniteMob()) - message_admins("[key_name_admin(user)] set [key_name_admin(M)] on fire") - log_game("[key_name(user)] set [key_name(M)] on fire") - var/obj/item/clothing/mask/cigarette/cig = help_light_cig(M) - if(lit && cig && user.a_intent == INTENT_HELP) - if(cig.lit) - to_chat(user, "[cig] is already lit.") - if(M == user) - cig.attackby(src, user) - else - if(istype(src, /obj/item/match/unathi)) - if(prob(50)) - cig.light("[user] spits fire at [M], lighting [cig] and nearly burning [user.p_their()] face!") - matchburnout() - else - cig.light("[user] spits fire at [M], burning [user.p_their()] face and lighting [cig] in the process.") - var/obj/item/organ/external/head/affecting = M.get_organ("head") - affecting.receive_damage(0, 5) - M.UpdateDamageIcon() - playsound(user.loc, 'sound/effects/unathiignite.ogg', 40, FALSE) - - else - cig.light("[user] holds [src] out for [M], and lights [cig].") - playsound(src, 'sound/items/lighter/light.ogg', 25, TRUE) +/obj/item/match/attack(mob/living/target, mob/living/user) + if(cigarette_lighter_act(user, target)) + return + + if(lit && target.IgniteMob()) + message_admins("[key_name_admin(user)] set [key_name_admin(target)] on fire") + log_game("[key_name(user)] set [key_name(target)] on fire") + + return ..() + +/obj/item/match/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + + // Otherwise the later parts of this proc can be passed to the unathi's blaze and cause a runtime. + if(is_unathi_fire) + return cig + + if(!cig) + return !isnull(cig) + + if(!lit) + to_chat(user, "You need to light [src] before it can be used to light anything!") + return TRUE + + if(target == user) + user.visible_message( + "[user] lights [user.p_their()] [cig] with [src].", + "You light [cig] with [src]." + ) else - ..() + user.visible_message( + "[user] holds [src] out for [target], and lights [cig].", + "You hold [src] out for [target], and light [user.p_their()] [cig]." + ) + cig.light(user, target) + matchburnout() + return TRUE /obj/item/match/decompile_act(obj/item/matter_decompiler/C, mob/user) if(isdrone(user) && burnt) @@ -305,11 +360,6 @@ return TRUE return ..() -/obj/item/proc/help_light_cig(mob/living/M) - var/mask_item = M.get_item_by_slot(SLOT_HUD_WEAR_MASK) - if(istype(mask_item, /obj/item/clothing/mask/cigarette)) - return mask_item - /obj/item/match/get_heat() return lit * 1000 @@ -332,6 +382,43 @@ origin_tech = null lit = TRUE w_class = WEIGHT_CLASS_BULKY //to prevent it going to pockets + is_unathi_fire = TRUE + +/obj/item/match/unathi/cigarette_lighter_act(mob/living/target, mob/living/user, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!lit) + to_chat(user, "If you can see this message, please make an issue report to GitHub, something bad has happened.") + return TRUE + + if(target == user) + user.visible_message( + "[user] spits fire at [user.p_their()] [cig.name], igniting it.", + "You spit fire at [cig], igniting it.", + "You hear a brief burst of flame!" + ) + else + if(prob(50)) + user.visible_message( + "[user] spits fire at [target], lighting [cig] in [target.p_their()] mouth and nearly burning [target.p_their()] face!", + "You spit fire at [target], lighting [cig] in [target.p_their()] mouth and nearly burning [target.p_their()] face!", + "You hear a brief burst of flame!" + ) + else + user.visible_message( + "[user] spits fire at [target], burning [target.p_their()] face and lighting [cig] in the process!", + "You spit fire at [target], burning [target.p_their()] face and lighting [cig] in the process!", + "You hear a brief burst of flame!" + ) + var/obj/item/organ/external/head/affecting = target.get_organ("head") + affecting.receive_damage(0, 5) + target.UpdateDamageIcon() + cig.light(user, target) + playsound(user.loc, 'sound/effects/unathiignite.ogg', 40, FALSE) + matchburnout() + return TRUE /obj/item/match/unathi/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/weapons/melee/energy_melee_weapons.dm b/code/game/objects/items/weapons/melee/energy_melee_weapons.dm index 3e9422d4bfeb..5fef9bf2214e 100644 --- a/code/game/objects/items/weapons/melee/energy_melee_weapons.dm +++ b/code/game/objects/items/weapons/melee/energy_melee_weapons.dm @@ -44,6 +44,8 @@ light_power = 2 var/brightness_on = 2 var/colormap = list(red=LIGHT_COLOR_RED, blue=LIGHT_COLOR_LIGHTBLUE, green=LIGHT_COLOR_GREEN, purple=LIGHT_COLOR_PURPLE, rainbow=LIGHT_COLOR_WHITE) + /// Used to mark the item as a cleaving saw so that cigarette_lighter_act() will perform an early return. + var/is_a_cleaving_saw = FALSE /obj/item/melee/energy/Initialize(mapload) . = ..() @@ -55,7 +57,10 @@ UnregisterSignal(src, COMSIG_ITEM_SHARPEN_ACT) return ..() -/obj/item/melee/energy/attack(mob/living/target, mob/living/carbon/human/user) +/obj/item/melee/energy/attack(mob/living/target, mob/living/user) + if(cigarette_lighter_act(user, target)) + return + var/nemesis_faction = FALSE if(LAZYLEN(nemesis_factions)) for(var/F in target.faction) @@ -68,6 +73,37 @@ if(nemesis_faction) force -= faction_bonus_force +/obj/item/melee/energy/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + if(is_a_cleaving_saw) + return FALSE + + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!HAS_TRAIT(src, TRAIT_ITEM_ACTIVE)) + to_chat(user, "You must activate [src] before you can light [cig] with it!") + return TRUE + + if(target == user) + user.visible_message( + "[user] makes a violent slashing motion, barely missing [user.p_their()] nose as light flashes! \ + [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [cig] with [src] in the process.", + "You casually slash [src] at [cig], lighting it with the blade.", + "You hear an energy blade slashing something!" + ) + else + user.visible_message( + "[user] makes a violent slashing motion, barely missing the nose of [target] as light flashes! \ + [user.p_they(TRUE)] light[user.p_s()] [cig] in the mouth of [target] with [src] in the process.", + "You casually slash [src] at [cig] in the mouth of [target], lighting it with the blade.", + "You hear an energy blade slashing something!" + ) + user.do_attack_animation(target) + playsound(user.loc, hitsound, 50, TRUE) + cig.light(user, target) + return TRUE + /obj/item/melee/energy/suicide_act(mob/user) user.visible_message(pick("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!", \ "[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!")) @@ -365,6 +401,7 @@ icon_state = "cleaving_saw" icon_state_on = "cleaving_saw_open" slot_flags = SLOT_FLAG_BELT + is_a_cleaving_saw = TRUE var/attack_verb_off = list("attacked", "sawed", "sliced", "torn", "ripped", "diced", "cut") attack_verb_on = list("cleaved", "swiped", "slashed", "chopped") hitsound = 'sound/weapons/bladeslice.ogg' diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index a383b313f3b2..6ec53ab55e5f 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -937,8 +937,8 @@ //Solgov /obj/item/storage/backpack/ert/solgov - name = "\improper TSF marine backpack" - desc = "A spacious backpack with lots of pockets, worn by marines of the Trans-Solar Federation." + name = "\improper TSF marine rucksack" + desc = "A spacious rucksack covered in pouches and pockets, worn by marines of the Trans-Solar Federation." icon_state = "ert_solgov" /obj/item/storage/backpack/ert/deathsquad diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm index d44683a5b997..49f940b18350 100644 --- a/code/game/objects/items/weapons/storage/fancy.dm +++ b/code/game/objects/items/weapons/storage/fancy.dm @@ -235,39 +235,45 @@ /obj/item/storage/fancy/cigarettes/update_icon_state() icon_state = "[initial(icon_state)][length(contents)]" -/obj/item/storage/fancy/cigarettes/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) +/obj/item/storage/fancy/cigarettes/attack(mob/living/carbon/M, mob/living/user) if(!ismob(M)) return - if(istype(M) && M == user && user.zone_selected == "mouth" && length(contents) > 0 && !user.wear_mask) - var/got_cig = 0 - for(var/num=1, num <= length(contents), num++) + if(istype(M) && user.zone_selected == "mouth" && length(contents) > 0 && !M.wear_mask) + var/got_cig = FALSE + for(var/num in 1 to length(contents)) var/obj/item/I = contents[num] if(istype(I, /obj/item/clothing/mask/cigarette)) var/obj/item/clothing/mask/cigarette/C = I - user.equip_to_slot_if_possible(C, SLOT_HUD_WEAR_MASK) - to_chat(user, "You take \a [C.name] out of the pack.") + M.equip_to_slot_if_possible(C, SLOT_HUD_WEAR_MASK) + if(M != user) + user.visible_message( + "[user] takes \a [C.name] out of [src] and gives it to [M].", + "You take \a [C.name] out of [src] and give it to [M]." + ) + else + to_chat(user, "You take \a [C.name] out of the pack.") update_icon() - got_cig = 1 + got_cig = TRUE break if(!got_cig) to_chat(user, "There are no smokables in the pack!") else ..() -/obj/item/storage/fancy/cigarettes/can_be_inserted(obj/item/W as obj, stop_messages = 0) +/obj/item/storage/fancy/cigarettes/can_be_inserted(obj/item/W, stop_messages = FALSE) if(istype(W, /obj/item/match)) var/obj/item/match/M = W if(M.lit) if(!stop_messages) to_chat(usr, "Putting a lit [W] in [src] probably isn't a good idea.") - return 0 + return FALSE if(istype(W, /obj/item/lighter)) var/obj/item/lighter/L = W if(L.lit) if(!stop_messages) to_chat(usr, "Putting [W] in [src] while lit probably isn't a good idea.") - return 0 + return FALSE //if we get this far, handle the insertion checks as normal . = ..() diff --git a/code/game/objects/items/weapons/storage/garment.dm b/code/game/objects/items/weapons/storage/garment.dm index 3c31d657e18d..b931ea2a76c4 100644 --- a/code/game/objects/items/weapons/storage/garment.dm +++ b/code/game/objects/items/weapons/storage/garment.dm @@ -222,7 +222,7 @@ new /obj/item/clothing/suit/armor/vest/warden(src) new /obj/item/clothing/head/warden(src) new /obj/item/clothing/suit/armor/vest/warden/alt(src) - new /obj/item/clothing/head/beret/sec/warden(src) + new /obj/item/clothing/head/beret/warden(src) new /obj/item/clothing/under/rank/security/warden(src) new /obj/item/clothing/under/rank/security/formal/warden(src) new /obj/item/clothing/under/rank/security/warden/corporate(src) diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index 4d3204ce5d76..49f787392552 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -140,6 +140,9 @@ set_light(0) /obj/item/dualsaber/attack(mob/target, mob/living/user) + if(cigarette_lighter_act(user, target)) + return + if(HAS_TRAIT(user, TRAIT_HULK)) to_chat(user, "You grip the blade too hard and accidentally drop it!") if(HAS_TRAIT(src, TRAIT_WIELDED)) @@ -153,6 +156,33 @@ if((HAS_TRAIT(src, TRAIT_WIELDED)) && prob(50)) INVOKE_ASYNC(src, PROC_REF(jedi_spin), user) +/obj/item/dualsaber/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!HAS_TRAIT(src, TRAIT_WIELDED)) + to_chat(user, "You need to activate [src] before you can light anything with it!") + return TRUE + + if(target == user) + user.visible_message( + "[user] flips through the air and spins [src] wildly! It brushes against [user.p_their()] [cig] and sets it alight!", + "You flip through the air and twist [src] so it brushes against [cig], lighting it with the blade.", + "You hear an energy blade slashing something!" + ) + else + user.visible_message( + "[user] flips through the air and slashes at [user] with [src]! The blade barely misses, brushing against [user.p_their()] [cig] and setting it alight!", + "You flip through the air and slash [src] at [cig], lighting it for [target].", + "You hear an energy blade slashing something!" + ) + user.do_attack_animation(target) + playsound(user.loc, hitsound, 50, TRUE) + cig.light(user, target) + INVOKE_ASYNC(src, PROC_REF(jedi_spin), user) + return TRUE + /obj/item/dualsaber/proc/jedi_spin(mob/living/user) for(var/i in list(NORTH, SOUTH, EAST, WEST, EAST, SOUTH, NORTH, SOUTH, EAST, WEST, EAST, SOUTH)) user.setDir(i) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 1e5b6d38159f..41affd7a4fc4 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -251,7 +251,7 @@ var/silent_removal = FALSE /obj/structure/alien/weeds/Initialize(mapload, node) - ..() + . = ..() linked_node = node if(isspaceturf(loc)) qdel(src) @@ -435,7 +435,7 @@ /obj/structure/alien/weeds/node/Initialize() add_overlay("weednode") - ..(loc, src) + return ..(loc, src) #undef NODERANGE diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm index 9e8e7d49a9e9..dddf8e330afb 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm @@ -232,3 +232,20 @@ new /obj/item/reagent_containers/glass/bottle/reagent/acetone(src) new /obj/item/reagent_containers/glass/bottle/reagent/acid(src) new /obj/item/reagent_containers/glass/bottle/reagent/diethylamine(src) + +/obj/structure/closet/secure_closet/genetics + name = "genetics test subject locker" + desc = "Storage for lesser form cubes." + icon_state = "genetics" + open_door_sprite = "med_door" + icon_opened = "med_open" + req_access = list(ACCESS_GENETICS) + +/obj/structure/closet/secure_closet/genetics/populate_contents() + new /obj/item/storage/box/monkeycubes(src) + new /obj/item/storage/box/monkeycubes/farwacubes(src) + new /obj/item/storage/box/monkeycubes/neaeracubes(src) + new /obj/item/storage/box/monkeycubes/nian_worme_cubes(src) + new /obj/item/storage/box/monkeycubes/stokcubes(src) + new /obj/item/storage/box/monkeycubes/wolpincubes(src) + diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index b79918fa9d83..a44a1b2ad805 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -34,7 +34,7 @@ . += "Alt-Click to deflate [src]." /obj/structure/inflatable/Initialize(location) - ..() + . = ..() recalculate_atmos_connectivity() /obj/structure/inflatable/Destroy() diff --git a/code/game/objects/structures/reflector.dm b/code/game/objects/structures/reflector.dm index 9d58f51557ba..b124b60dece9 100644 --- a/code/game/objects/structures/reflector.dm +++ b/code/game/objects/structures/reflector.dm @@ -5,6 +5,7 @@ desc = "A frame to create a reflector.\nUse 5 sheets of glass to create a 1 way reflector.\nUse 10 sheets of reinforced glass to create a 2 way reflector.\nUse 1 diamond to create a reflector cube." anchored = FALSE density = TRUE + max_integrity = 50 layer = 3 var/finished = FALSE var/obj/item/stack/sheet/build_stack_type @@ -17,7 +18,7 @@ if(!istype(P, /obj/item/projectile/beam)) return ..() var/new_dir = get_reflection(dir, P.dir) - if(new_dir) + if(new_dir && anchored) reflect_turf = get_step(reflect_turf, new_dir) else visible_message("[src] is hit by [P]!") @@ -90,13 +91,13 @@ return if(anchored) WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + if(!I.use_tool(src, user, 5 SECONDS, volume = I.tool_volume)) return WELDER_FLOOR_SLICE_SUCCESS_MESSAGE anchored = FALSE else WELDER_ATTEMPT_FLOOR_WELD_MESSAGE - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + if(!I.use_tool(src, user, 5 SECONDS, volume = I.tool_volume)) return WELDER_FLOOR_WELD_SUCCESS_MESSAGE anchored = TRUE diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 5c9f02a76ead..92dd37c15e14 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -328,22 +328,51 @@ if(user.stat || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED) || !Adjacent(user) || !can_be_flipped || is_ventcrawling(user)) return + var/flip_speed = get_flip_speed(user) + if(!flipped) + + if(flip_speed > 0) + user.visible_message("[user] starts trying to flip [src]!", "You start trying to flip [src][flip_speed >= 5 SECONDS ? " (it'll take about [flip_speed / 10] seconds)." : ""].") + if(!do_after(user, flip_speed, TRUE, src)) + user.visible_message("[user] gives up on trying to flip [src].") + return if(!flip(get_cardinal_dir(user, src))) to_chat(user, "It won't budge.") return - user.visible_message("[user] flips \the [src]!") + + user.visible_message("[user] flips [src]!") if(climbable) structure_shaken() else + if(flip_speed > 0) + user.visible_message("[user] starts trying to right [src]!", "You start trying to right [src][flip_speed >= 5 SECONDS ? " (it'll take about [flip_speed / 10] seconds)." : ""]") + if(!do_after(user, flip_speed, TRUE, src)) + user.visible_message("[user] gives up on trying to right [src].") + return if(!unflip()) to_chat(user, "It won't budge.") + user.visible_message("[user] rights [src]!") + +/obj/structure/table/proc/get_flip_speed(mob/living/flipper) + if(!istype(flipper)) + return 0 SECONDS // sure + if(!issimple_animal(flipper)) + return 0 SECONDS + switch(flipper.mob_size) + if(MOB_SIZE_TINY) + return 30 SECONDS // you can do it but you gotta *really* work for it + if(MOB_SIZE_SMALL) + return 5 SECONDS // not gonna terrorize anything + else + return 0 SECONDS + /obj/structure/table/proc/flip(direction) if(flipped) - return FALSE + return 0 if(!straight_table_check(turn(direction, 90)) || !straight_table_check(turn(direction, -90))) return FALSE diff --git a/code/game/turfs/simulated/floor/asteroid_floors.dm b/code/game/turfs/simulated/floor/asteroid_floors.dm index b898f7b74850..cdf971a9bdff 100644 --- a/code/game/turfs/simulated/floor/asteroid_floors.dm +++ b/code/game/turfs/simulated/floor/asteroid_floors.dm @@ -416,6 +416,7 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me megafauna_spawn_list.Remove(randumb) new randumb(T) + SSblackbox.record_feedback("tally", "lavaland_mob_spawns", 1, "[randumb]") #undef SPAWN_MEGAFAUNA #undef SPAWN_BUBBLEGUM diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm index 2febfc20d78c..7c9034b9ea5c 100644 --- a/code/game/turfs/simulated/river.dm +++ b/code/game/turfs/simulated/river.dm @@ -67,21 +67,25 @@ var/detouring = 0 var/cur_dir = get_dir(cur_turf, target_turf) while(cur_turf != target_turf) - if(detouring) //randomly snake around a bit - if(prob(21)) - detouring = 0 - cur_dir = get_dir(cur_turf, target_turf) - else if(prob(20)) - detouring = 1 - if(prob(50)) - cur_dir = turn(cur_dir, 45) + var/attempts = 100 + do + if(detouring) //randomly snake around a bit + if(prob(20)) + detouring = 0 + cur_dir = get_dir(cur_turf, target_turf) + else if(prob(20)) + detouring = 1 + if(prob(50)) + cur_dir = turn(cur_dir, 45) + else + cur_dir = turn(cur_dir, -45) else - cur_dir = turn(cur_dir, -45) - else - cur_dir = get_dir(cur_turf, target_turf) + cur_dir = get_dir(cur_turf, target_turf) + // we may veer off the map entirely, returning a null turf; if so, go back and try again + while(get_step(cur_turf, cur_dir) == null && attempts-- > 0) cur_turf = get_step(cur_turf, cur_dir) - if(isnull(cur_turf)) //This might be the fuck up. Kill the loop if this happens + if(isnull(cur_turf)) stack_trace("Encountered a null turf in river loop, target turf was [target_turf], x=[target_turf.x], y=[target_turf.y].") break var/area/new_area = get_area(cur_turf) diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm index 75ca819c27a8..ec58c1c01a66 100644 --- a/code/game/verbs/who.dm +++ b/code/game/verbs/who.dm @@ -3,36 +3,36 @@ set category = "OOC" var/list/lines = list() - var/list/temp = list() + var/list/ckeys = list() for(var/client/C in GLOB.clients) if(C.holder && C.holder.big_brother) // BB doesn't show up at all continue if(C.holder && C.holder.fakekey) - temp += C.holder.fakekey + ckeys += C.holder.fakekey else - temp += C.key + ckeys += C.key - temp = sortList(temp) // Sort it. We dont do this above because fake keys would be out of order, which would be a giveaway + ckeys = sortList(ckeys) // Sort it. We dont do this above because fake keys would be out of order, which would be a giveaway var/list/output_players = list() // Now go over it again to apply colours. - for(var/p in temp) - var/client/C = GLOB.directory[ckey(p)] + for(var/player in ckeys) + var/client/C = GLOB.directory[ckey(player)] if(!C) // This should NEVER happen, but better to be safe continue // Get the colour var/colour = client2rankcolour(C) - var/out = "[p]" + var/output = "[player]" if(C.holder) - out = "[out]" + output = "[output]" if(colour) - out = "[out]" + output = "[output]" - output_players += out + output_players += output lines += "Current Players ([length(output_players)]): " lines += output_players.Join(", ") // Turn players into a comma separated list @@ -40,9 +40,7 @@ if(check_rights(R_ADMIN, FALSE)) lines += "Click here for detailed (old) who." - var/msg = lines.Join("\n") - - to_chat(src, msg) + to_chat(src, lines.Join("
")) // Advanced version of `who` to show player age, antag status and more. Lags the chat when loading, so its in its own proc /client/proc/who_advanced() @@ -55,7 +53,8 @@ if(C.holder && C.holder.big_brother && !check_rights(R_PERMISSIONS, FALSE)) // need PERMISSIONS to see BB continue - var/entry = "\t[C.key]" + var/list/entry = list() + entry += "\t[C.key]" if(C.holder && C.holder.fakekey) entry += " (as [C.holder.fakekey])" entry += " - Playing as [C.mob.real_name]" @@ -64,8 +63,8 @@ entry += " - Unconscious" if(DEAD) if(isobserver(C.mob)) - var/mob/dead/observer/O = C.mob - if(O.started_as_observer) + var/mob/dead/observer/observer = C.mob + if(observer.started_as_observer) entry += " - Observing" else entry += " - DEAD" @@ -74,113 +73,97 @@ else entry += " - DEAD" - var/age + var/account_age if(isnum(C.player_age)) - age = C.player_age + account_age = C.player_age else - age = 0 + account_age = 0 - if(age <= 1) - age = "[age]" - else if(age < 10) - age = "[age]" + if(account_age <= 1) + account_age = "[account_age]" + else if(account_age < 10) + account_age = "[account_age]" - entry += " - [age]" + entry += " - [account_age]" if(is_special_character(C.mob)) entry += " - Antagonist" entry += " ([ADMIN_QUE(C.mob, "?")])" - Lines += entry - var/msg = "" + Lines += entry.Join("") + + var/list/msg = list() for(var/line in sortList(Lines)) - msg += "[line]\n" + msg += "[line]" msg += "Total Players: [length(Lines)]" - to_chat(src, msg) + to_chat(src, msg.Join("
")) /client/verb/adminwho() set category = "Admin" set name = "Adminwho" - var/msg = "" - var/modmsg = "" - var/num_mods_online = 0 + var/list/adminmsg = list() + var/list/mentormsg = list() + var/list/devmsg = list() + var/num_mentors_online = 0 var/num_admins_online = 0 - if(holder) - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN, 0, C.mob)) - - if(C?.holder?.fakekey && !check_rights(R_ADMIN, 0)) //Mentors can't see stealthmins - continue - - if(C?.holder?.big_brother && !check_rights(R_PERMISSIONS, FALSE)) // normal admins can't see BB - continue - - // Their rank may not have a defined colour, only set colour if so - var/rank_colour = client2rankcolour(C) - if(rank_colour) - msg += "[C] is a [C.holder.rank]" - else - msg += "[C] is a [C.holder.rank]" - - if(C.holder.fakekey) - msg += " (as [C.holder.fakekey])" - - if(isobserver(C.mob)) - msg += " - Observing" - else if(isnewplayer(C.mob)) - msg += " - Lobby" - else - msg += " - Playing" + var/num_devs_online = 0 - if(C.is_afk()) - msg += " (AFK)" - msg += "\n" - - num_admins_online++ - - else if(check_rights(R_MENTOR|R_MOD, 0, C.mob)) - // Their rank may not have a defined colour, only set colour if so - var/rank_colour = client2rankcolour(C) - if(rank_colour) - modmsg += "[C] is a [C.holder.rank]" - else - modmsg += "[C] is a [C.holder.rank]" - - if(isobserver(C.mob)) - modmsg += " - Observing" - else if(isnewplayer(C.mob)) - modmsg += " - Lobby" - else - modmsg += " - Playing" - - if(C.is_afk()) - modmsg += " (AFK)" - modmsg += "\n" - num_mods_online++ - else - for(var/client/C in GLOB.admins) - - if(check_rights(R_ADMIN, 0, C.mob)) - if(!C.holder.fakekey) - var/rank_colour = client2rankcolour(C) - if(rank_colour) - msg += "[C] is a [C.holder.rank]" - else - msg += "[C] is a [C.holder.rank]" - msg += "\n" - num_admins_online++ - else if(check_rights(R_MOD|R_MENTOR, 0, C.mob) && !check_rights(R_ADMIN, 0, C.mob)) - var/rank_colour = client2rankcolour(C) - if(rank_colour) - modmsg += "[C] is a [C.holder.rank]" - else - modmsg += "[C] is a [C.holder.rank]" - modmsg += "\n" - num_mods_online++ - - var/noadmins_info = "\nIf no admins or mentors are online, make a ticket anyways. Adminhelps and mentorhelps will be relayed to discord, and staff will still be informed." - msg = "Current Admins ([num_admins_online]):\n" + msg + "\nCurrent Mentors ([num_mods_online]):\n" + modmsg + noadmins_info - to_chat(src, msg) + for(var/client/C in GLOB.admins) + var/list/line = list() + var/rank_colour = client2rankcolour(C) + if(rank_colour) + line += "[C] is a [C.holder.rank]" + else + line += "[C] is a [C.holder.rank]" + + if(holder) // Only for those with perms see the extra bit + if(C.holder.fakekey && check_rights(R_ADMIN, FALSE)) + line += " (as [C.holder.fakekey])" + + if(isobserver(C.mob)) + line += " - Observing" + else if(isnewplayer(C.mob)) + line += " - Lobby" + else + line += " - Playing" + + if(C.is_afk()) + line += " (AFK)" + + line += "
" + if(check_rights(R_ADMIN, FALSE, C.mob)) // Is this client an admin? + if(C?.holder?.fakekey && !check_rights(R_ADMIN, FALSE)) // Only admins can see stealthmins + continue + + if(C?.holder?.big_brother && !check_rights(R_PERMISSIONS, FALSE)) // Normal admins can't see Big Brother + continue + num_admins_online++ + adminmsg += line.Join("") + + else if(check_rights(R_DEV_TEAM, FALSE, C.mob)) // Is this client a developer? + num_devs_online++ + devmsg += line.Join("") + + else if(check_rights(R_MENTOR, FALSE, C.mob)) // Is this client a mentor? + num_mentors_online++ + mentormsg += line.Join("") + + var/list/final_message = list() + if(num_admins_online) + final_message += "Current Admins ([num_admins_online]):
" + final_message += adminmsg + final_message += "
" + if(num_devs_online) + final_message += "Current Developers ([num_devs_online]):
" + final_message += devmsg + final_message += "
" + if(num_mentors_online) + final_message += "Current Mentors ([num_mentors_online]):
" + final_message += mentormsg + final_message += "
" + if(!num_admins_online || !num_mentors_online) + final_message += "Even with no [!num_admins_online ? "admins" : ""][!num_admins_online && !num_mentors_online ? " or " : ""][!num_mentors_online ? "mentors" : ""] are online, make a ticket anyways. [!num_admins_online ? "Adminhelps" : ""][!num_admins_online && !num_mentors_online ? " and " : ""][!num_mentors_online ? "Mentorhelps" : ""] will be relayed to discord, and staff will still be informed." + to_chat(src, final_message.Join("")) diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index c0cbf6dd0526..e6d6844116ce 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -33,6 +33,7 @@ GLOBAL_PROTECT(admin_ranks) // this shit is being protected for obvious reasons if("spawn","create") rights |= R_SPAWN if("mod") rights |= R_MOD if("mentor") rights |= R_MENTOR + if("developer") rights |= R_DEV_TEAM if("proccall") rights |= R_PROCCALL if("viewruntimes") rights |= R_VIEWRUNTIMES if("maintainer") rights |= R_MAINTAINER diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index fce204147890..e570dcc5c649 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -51,6 +51,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/library_manager, /client/proc/view_asays, /client/proc/view_msays, + /client/proc/view_devsays, /client/proc/empty_ai_core_toggle_latejoin, /client/proc/aooc, /client/proc/freeze, @@ -1179,7 +1180,7 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( message_admins("[key_name_admin(usr)] is visualising interesting atmos turfs. Server may lag.") var/list/zlevel_turf_indexes = list() - + var/list/coords = get_interesting_atmos_tiles() if(!length(coords)) to_chat(mob, "There are no interesting turfs. How interesting!") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 5fac4cafa053..00757650a39d 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -272,7 +272,7 @@ if(length(GLOB.admin_ranks)) new_rank = input("Please select a rank", "New rank", null, null) as null|anything in (GLOB.admin_ranks|"*New Rank*") else - new_rank = input("Please select a rank", "New rank", null, null) as null|anything in list("Mentor", "Trial Admin", "Game Admin", "*New Rank*") + new_rank = input("Please select a rank", "New rank", null, null) as null|anything in list("Mentor", "Trial Admin", "Game Admin", "Developer", "*New Rank*") var/rights = 0 if(D) @@ -1275,6 +1275,12 @@ usr.client.view_msays() + else if(href_list["devsays"]) + if(!check_rights(R_ADMIN | R_DEV_TEAM)) + return + + usr.client.view_devsays() + else if(href_list["tdome1"]) if(!check_rights(R_SERVER|R_EVENT)) return diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 924a7b9c0527..f01ec5578478 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -45,6 +45,48 @@ var/msg = input(src, null, "msay \"text\"") as text|null cmd_mentor_say(msg) +/client/proc/get_dev_team_say() + if(check_rights(R_DEV_TEAM | R_ADMIN | R_MOD)) + var/msg = input(src, null, "devsay \"text\"") as text|null + cmd_dev_say(msg) + +/client/proc/cmd_dev_say(msg as text) + set name = "Devsay" + set hidden = TRUE + + if(!check_rights(R_ADMIN|R_DEV_TEAM)) // Catch any non-admins trying to use this proc + return + + msg = emoji_parse(copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)) + + if(!msg) + return + + log_devsay(msg, src) + var/datum/say/devsay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday) + GLOB.devsays += devsay + mob.create_log(OOC_LOG, "DEVSAY: [msg]") + + if(SSredis.connected) + var/list/data = list() + data["author"] = usr.ckey + data["source"] = GLOB.configuration.system.instance_id + data["message"] = html_decode(msg) + SSredis.publish("byond.devsay", json_encode(data)) + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD|R_DEV_TEAM, 0, C.mob)) + var/display_name = key + if(holder.fakekey) + if(C.holder && C.holder.rights & R_ADMIN) + display_name = "[holder.fakekey]/([key])" + else + display_name = holder.fakekey + msg = "[msg]" + to_chat(C, "DEV: [display_name] ([admin_jump_link(mob)]): [msg]", MESSAGE_TYPE_DEVCHAT, confidential = TRUE) + + SSblackbox.record_feedback("tally", "admin_verb", 1, "Devsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + /client/proc/cmd_mentor_say(msg as text) set name = "Msay" set hidden = 1 diff --git a/code/modules/admin/verbs/asays.dm b/code/modules/admin/verbs/asays.dm index 7fe02d91ce13..eedfad493215 100644 --- a/code/modules/admin/verbs/asays.dm +++ b/code/modules/admin/verbs/asays.dm @@ -1,5 +1,6 @@ GLOBAL_LIST_EMPTY(asays) GLOBAL_LIST_EMPTY(msays) +GLOBAL_LIST_EMPTY(devsays) /datum/say var/ckey @@ -23,6 +24,16 @@ GLOBAL_LIST_EMPTY(msays) display_says(GLOB.msays, "msay") +/client/proc/view_devsays() + set name = "Devsays" + set desc = "View Devsays from the current round." + set category = "Admin" + + if(!check_rights(R_DEV_TEAM | R_ADMIN)) + return + + display_says(GLOB.devsays, "devsay") + /client/proc/view_asays() set name = "Asays" set desc = "View Asays from the current round." diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 6a2056c03725..ff4dc2e292e6 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -25,6 +25,9 @@ if(check_rights(R_MENTOR, 0)) stafftype = "MENTOR" + if(check_rights(R_DEV_TEAM, 0)) + stafftype = "DEVELOPER" + if(check_rights(R_MOD, 0)) stafftype = "MOD" diff --git a/code/modules/admin/verbs/freeze.dm b/code/modules/admin/verbs/freeze.dm index 988c2df44b21..2b90556e0187 100644 --- a/code/modules/admin/verbs/freeze.dm +++ b/code/modules/admin/verbs/freeze.dm @@ -64,6 +64,12 @@ GLOBAL_LIST_EMPTY(frozen_atom_list) // A list of admin-frozen atoms. revive() /mob/living/simple_animal/admin_Freeze(admin) + // If we were frozen before this call, make sure we + // reset our health before attempting a rejuvenate, + // as removing status effects can perform stat calls. + if(frozen && del_on_death) + health = admin_prev_health + if(..()) // The result of the parent call here will be the value of the mob's `frozen` variable after they get (un)frozen. admin_prev_health = health health = 0 diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index dd9c6f9ec053..0f09929b83a1 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -28,7 +28,10 @@ var/turf/location = get_turf(loc) if(location) location.hotspot_expose(1000, 1000) - + visible_message( + "Sparks shoot out of [src].", + "You hear a shower of sparks shooting out from something!" + ) sparks.start() if(istype(loc, /obj/item/assembly_holder)) @@ -54,6 +57,30 @@ return TRUE +/obj/item/assembly/igniter/attack(mob/living/target, mob/living/user) + if(!cigarette_lighter_act(user, target)) + return ..() + +/obj/item/assembly/igniter/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(target == user) + user.visible_message( + "[user] presses [src] against [cig] and activates it, lighting [cig] in a shower of sparks!", + "You press [src] against [cig] and activates it, lighting [cig] in a shower of sparks!", + "You hear a shower of sparks shooting out from something!" + ) + else + user.visible_message( + "[user] presses [src] against [cig] and activates it, lighting [cig] for [target] in a shower of sparks!", + "You press [src] against [cig] and activate it, lighting [cig] in a shower of sparks!", + "You hear a shower of sparks shooting out from something!" + ) + sparks.start() // Make sparks fly! + cig.light(user, target) + return TRUE /obj/item/assembly/igniter/attack_self(mob/user) if(!istype(loc, /obj/item/assembly_holder)) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm index 731d9c8ccbac..482fad1a0d9b 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm @@ -147,7 +147,7 @@ if(target.initialize_directions & get_dir(target,src)) node = target break - build_network() + initialize_atmos_network() update_icon() /obj/machinery/atmospherics/unary/thermomachine/attack_ai(mob/user) diff --git a/code/modules/awaymissions/mission_code/ruins/abandoned_engi_sat.dm b/code/modules/awaymissions/mission_code/ruins/abandoned_engi_sat.dm new file mode 100644 index 000000000000..2908f0b65f23 --- /dev/null +++ b/code/modules/awaymissions/mission_code/ruins/abandoned_engi_sat.dm @@ -0,0 +1,13 @@ +/obj/structure/closet/secure_closet/engineering_personal/empty + locked = FALSE + opened = TRUE + +/obj/structure/closet/secure_closet/engineering_personal/empty/populate_contents() + return + +/obj/structure/closet/wardrobe/atmospherics_yellow/empty + opened = TRUE + +/obj/structure/closet/wardrobe/atmospherics_yellow/populate_contents() + return + diff --git a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm index 8dc9de2314bb..17494928b3ff 100644 --- a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm +++ b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm @@ -7,12 +7,13 @@ icon = 'icons/mob/fleshling.dmi' icon_state = "fleshling" icon_living = "fleshling" + attack_sound = 'sound/misc/demon_attack1.ogg' + death_sound = 'sound/misc/demon_dies.ogg' icon_dead = "" speed = 5 move_to_delay = 4 ranged = TRUE pixel_x = -16 - attack_sound = 'sound/misc/demon_attack1.ogg' melee_damage_lower = 20 melee_damage_upper = 20 wander = TRUE @@ -23,8 +24,6 @@ a_intent = INTENT_HARM deathmessage = "collapses into a pile of gibs. From the looks of it this is the deadest it can get... " del_on_death = TRUE - death_sound = 'sound/misc/demon_dies.ogg' - attack_sound = 'sound/misc/demon_attack1.ogg' /// Is the boss charging right now? var/charging = FALSE diff --git a/code/modules/awaymissions/mission_code/ruins/oldstation.dm b/code/modules/awaymissions/mission_code/ruins/oldstation.dm index 9d11b96f6eee..c8e4989baab8 100644 --- a/code/modules/awaymissions/mission_code/ruins/oldstation.dm +++ b/code/modules/awaymissions/mission_code/ruins/oldstation.dm @@ -178,7 +178,7 @@ name = "\improper Crew Reawakening Report" /obj/item/paper/fluff/ruins/oldstation/report/Initialize() - ..() + . = ..() init_current_date_string() info = "Artificial Program's report to surviving crewmembers.

Crew were placed into cryostasis 10 March, 2445.

Crew were awoken from cryostasis [GLOB.current_date_string].

\ SIGNIFICANT EVENTS OF NOTE
1: The primary radiation detectors were taken offline after [GLOB.game_year - 2445] years due to power failure, secondary radiation detectors showed no residual \ diff --git a/code/modules/awaymissions/mission_code/ruins/telecomns.dm b/code/modules/awaymissions/mission_code/ruins/telecomns.dm index b5fdd54a6bc1..0d33c92bc9fa 100644 --- a/code/modules/awaymissions/mission_code/ruins/telecomns.dm +++ b/code/modules/awaymissions/mission_code/ruins/telecomns.dm @@ -59,6 +59,7 @@ GLOBAL_LIST_EMPTY(telecomms_trap_tank) name = "recharger" desc = "A charging dock for energy based weaponry. Did it just-" icon_state = "autolathe_trap" + board_type = /obj/item/circuitboard/autolathe/trapped //Has someone put an item in the autolathe, breaking the hologram? var/disguise_broken = FALSE diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index eb0ce0a7132a..82d1a6fab601 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -227,3 +227,6 @@ /// The current fullscreen state for /client/toggle_fullscreen() var/fullscreen = FALSE + + /// Cache of MD5'd UIDs. This is to stop clients from poking at object UIDs and being exploity with them + var/list/m5_uid_cache = list() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 4c256d0dc3ac..47b1278de7ec 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -49,6 +49,14 @@ var/hsrc_info = datum_info_line(hsrc) || "[hsrc]" stack_trace("Got \\ref-based src in topic from [src] for [hsrc_info], should be UID: [href]") + if(hsrc == src && href_list["m5src"]) + // We found an MD5'd UID, get the REAL thing + var/datum/D = locateUID(m5_uid_cache[href_list["m5src"]]) + if(!istype(D)) + CRASH("Tried to find an item by MD5'd UID when it wasnt in the client cache!") + + hsrc = D + // asset_cache var/asset_cache_job diff --git a/code/modules/client/preference/loadout/loadout_accessories.dm b/code/modules/client/preference/loadout/loadout_accessories.dm index 7bcd626a0434..10cb910c97a8 100644 --- a/code/modules/client/preference/loadout/loadout_accessories.dm +++ b/code/modules/client/preference/loadout/loadout_accessories.dm @@ -203,17 +203,17 @@ /datum/gear/accessory/armband_job/cargo display_name = "Armband, cargo" path = /obj/item/clothing/accessory/armband/cargo - allowed_roles = list("Quartermaster","Cargo Technician", "Shaft Miner") + allowed_roles = list("Quartermaster","Cargo Technician", "Shaft Miner", "Explorer") /datum/gear/accessory/armband_job/medical display_name = "Armband, medical" path = /obj/item/clothing/accessory/armband/med - allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Coroner", "Paramedic") + allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Coroner", "Virologist", "Geneticist", "Paramedic") /datum/gear/accessory/armband_job/emt display_name = "Armband, EMT" path = /obj/item/clothing/accessory/armband/medgreen - allowed_roles = list("Paramedic") + allowed_roles = list("Chief Medical Officer", "Paramedic") /datum/gear/accessory/armband_job/engineering display_name = "Armband, engineering" @@ -228,14 +228,14 @@ /datum/gear/accessory/armband_job/sci display_name = "Armband, science" path = /obj/item/clothing/accessory/armband/science - allowed_roles = list("Research Director","Scientist", "Roboticist") + allowed_roles = list("Research Director","Scientist", "Roboticist", "Geneticist") /datum/gear/accessory/armband_job/procedure display_name = "Armband, procedure" path = /obj/item/clothing/accessory/armband/procedure - allowed_roles = list("Nanotrasen Representative", "Magistrate", "Internal Affairs Agent") + allowed_roles = list("Captain", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent") /datum/gear/accessory/armband_job/service display_name = "Armband, service" path = /obj/item/clothing/accessory/armband/service - allowed_roles = list("Head of Personnel", "Chaplain", "Janitor", "Botanist", "Chef", "Bartender", "Clown", "Mime", "Librarian", "Barber") + allowed_roles = list("Head of Personnel", "Chaplain", "Janitor", "Botanist", "Chef", "Bartender", "Clown", "Mime", "Librarian") diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm index f961117fa88f..325e37e5995f 100644 --- a/code/modules/client/preference/loadout/loadout_hat.dm +++ b/code/modules/client/preference/loadout/loadout_hat.dm @@ -185,7 +185,17 @@ /datum/gear/hat/beret_job/sec display_name = "Beret, security" path = /obj/item/clothing/head/beret/sec - allowed_roles = list("Head of Security", "Warden", "Security Officer") + allowed_roles = list("Head of Security", "Warden", "Security Officer", "Detective") + +/datum/gear/hat/beret_job/warden + display_name = "Beret, warden" + path = /obj/item/clothing/head/beret/warden + allowed_roles = list("Warden") + +/datum/gear/hat/beret_job/hos + display_name = "Beret, head of security" + path = /obj/item/clothing/head/beret/hos + allowed_roles = list("Head of Security") /datum/gear/hat/beret_job/jani display_name = "Beret, janitor" @@ -200,7 +210,7 @@ /datum/gear/hat/beret_job/cargo display_name = "Beret, cargo" path = /obj/item/clothing/head/beret/cargo - allowed_roles = list("Quartermaster", "Cargo Technician") + allowed_roles = list("Quartermaster", "Cargo Technician", "Shaft Miner", "Explorer") /datum/gear/hat/beret_job/qm display_name = "Beret, quartermaster" @@ -210,7 +220,7 @@ /datum/gear/hat/beret_job/sci display_name = "Beret, science" path = /obj/item/clothing/head/beret/sci - allowed_roles = list("Research Director", "Scientist") + allowed_roles = list("Research Director", "Scientist", "Geneticist") /datum/gear/hat/beret_job/robowhite display_name = "Beret, robotics" @@ -225,7 +235,7 @@ /datum/gear/hat/beret_job/med display_name = "Beret, medical" path = /obj/item/clothing/head/beret/med - allowed_roles = list("Chief Medical Officer", "Medical Doctor" , "Virologist", "Coroner") + allowed_roles = list("Chief Medical Officer", "Medical Doctor" , "Virologist", "Geneticist", "Coroner", "Paramedic") /datum/gear/hat/beret_job/eng display_name = "Beret, engineering" diff --git a/code/modules/client/preference/loadout/loadout_suit.dm b/code/modules/client/preference/loadout/loadout_suit.dm index 3a483bbca70b..b4f36409d1c6 100644 --- a/code/modules/client/preference/loadout/loadout_suit.dm +++ b/code/modules/client/preference/loadout/loadout_suit.dm @@ -41,12 +41,12 @@ /datum/gear/suit/coat/job/med display_name = "Winter coat, medical" path = /obj/item/clothing/suit/hooded/wintercoat/medical - allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Chemist", "Psychiatrist", "Paramedic", "Virologist", "Coroner") + allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Chemist", "Psychiatrist", "Paramedic", "Virologist", "Coroner", "Geneticist") /datum/gear/suit/coat/job/sci display_name = "Winter coat, science" path = /obj/item/clothing/suit/hooded/wintercoat/science - allowed_roles = list("Scientist", "Research Director") + allowed_roles = list("Scientist", "Research Director", "Geneticist") /datum/gear/suit/coat/job/engi display_name = "Winter coat, engineering" @@ -129,7 +129,7 @@ /datum/gear/suit/bomber/job/medical display_name = "Bomber jacket, medical" path = /obj/item/clothing/suit/jacket/bomber/med - allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Chemist", "Psychiatrist", "Paramedic", "Virologist", "Coroner") + allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Chemist", "Psychiatrist", "Paramedic", "Virologist", "Coroner", "Geneticist") /datum/gear/suit/bomber/job/chemist display_name = "Bomber jacket, chemist" @@ -144,7 +144,7 @@ /datum/gear/suit/bomber/job/science display_name = "Bomber jacket, science" path = /obj/item/clothing/suit/jacket/bomber/sci - allowed_roles = list("Research Director", "Scientist") + allowed_roles = list("Research Director", "Scientist", "Geneticist") /datum/gear/suit/bomber/job/robotics display_name = "Bomber jacket, robotics" diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 5623d28449c2..dbd89bdeb7bd 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -157,6 +157,7 @@ I.item_state = initial(picked_item.item_state) I.item_color = initial(picked_item.item_color) + I.color = initial(picked_item.color) I.icon_override = initial(picked_item.icon_override) if(initial(picked_item.sprite_sheets)) diff --git a/code/modules/clothing/glasses/tajblind.dm b/code/modules/clothing/glasses/tajblind.dm index ea202e12dd41..7d5b9754e8c7 100644 --- a/code/modules/clothing/glasses/tajblind.dm +++ b/code/modules/clothing/glasses/tajblind.dm @@ -61,7 +61,7 @@ item_state = "tajblind_engi" /obj/item/clothing/glasses/hud/tajblind/meson/Initialize() - ..() + . = ..() desc += "
It has an optical meson scanner integrated into it." /obj/item/clothing/glasses/hud/tajblind/meson/equipped(mob/user, slot, initial) @@ -87,7 +87,7 @@ actions_types = list(/datum/action/item_action/toggle, /datum/action/item_action/toggle_research_scanner) /obj/item/clothing/glasses/hud/tajblind/sci/Initialize() - ..() + . = ..() desc += "
It has science goggles integrated into it." /obj/item/clothing/glasses/hud/tajblind/sci/item_action_slot_check(slot) @@ -102,7 +102,7 @@ examine_extensions = list(EXAMINE_HUD_MEDICAL_READ) /obj/item/clothing/glasses/hud/tajblind/med/Initialize() - ..() + . = ..() desc += "
It has a health HUD integrated into it." /obj/item/clothing/glasses/hud/tajblind/sec @@ -113,7 +113,7 @@ examine_extensions = list(EXAMINE_HUD_SECURITY_READ) /obj/item/clothing/glasses/hud/tajblind/sec/Initialize() - ..() + . = ..() desc += "
It has a security HUD integrated into it." /obj/item/clothing/glasses/hud/tajblind/shaded @@ -122,7 +122,7 @@ tint = FLASH_PROTECTION_FLASH /obj/item/clothing/glasses/hud/tajblind/shaded/Initialize() - ..() + . = ..() desc += "
It has an in-built flash protection." /obj/item/clothing/glasses/hud/tajblind/shaded/meson @@ -131,7 +131,7 @@ item_state = "tajblind_engi" /obj/item/clothing/glasses/hud/tajblind/shaded/meson/Initialize() - ..() + . = ..() desc += "
It has an optical meson scanner integrated into it." /obj/item/clothing/glasses/hud/tajblind/shaded/meson/equipped(mob/user, slot, initial) @@ -158,7 +158,7 @@ actions_types = list(/datum/action/item_action/toggle, /datum/action/item_action/toggle_research_scanner) /obj/item/clothing/glasses/hud/tajblind/shaded/sci/Initialize() - ..() + . = ..() desc += "
It has science goggles integrated into it." /obj/item/clothing/glasses/hud/tajblind/shaded/sci/item_action_slot_check(slot) @@ -173,7 +173,7 @@ examine_extensions = list(EXAMINE_HUD_MEDICAL_READ) /obj/item/clothing/glasses/hud/tajblind/shaded/med/Initialize() - ..() + . = ..() desc += "
It has a health HUD integrated into it." /obj/item/clothing/glasses/hud/tajblind/shaded/sec @@ -185,7 +185,7 @@ examine_extensions = list(EXAMINE_HUD_SECURITY_READ) /obj/item/clothing/glasses/hud/tajblind/shaded/sec/Initialize() - ..() + . = ..() desc += "
It has a security HUD integrated into it." #undef MODE_OFF diff --git a/code/modules/clothing/head/beret.dm b/code/modules/clothing/head/beret.dm index d1e6b2e45631..9832b2438b7b 100644 --- a/code/modules/clothing/head/beret.dm +++ b/code/modules/clothing/head/beret.dm @@ -84,7 +84,7 @@ strip_delay = 60 dog_fashion = /datum/dog_fashion/head/beret/sec -/obj/item/clothing/head/beret/sec/warden +/obj/item/clothing/head/beret/warden name = "warden's beret" desc = "A special beret with the Warden's insignia emblazoned on it. For wardens with class." icon_state = "beret_warden" @@ -180,7 +180,7 @@ //Special Roles /obj/item/clothing/head/beret/solgov/command - name = "\improper Trans-Solar Federation Lieutenant's beret" + name = "\improper TSF Lieutenant's beret" desc = "A beret worn by marines of the Trans-Solar Federation. The insignia signifies the wearer bears the rank of a Lieutenant." icon_state = "beret_solgovc" item_color = "solgovc" @@ -189,8 +189,8 @@ strip_delay = 80 /obj/item/clothing/head/beret/solgov/command/elite - name = "\improper Trans-Solar Federation Specops Lieutenant's beret" - desc = "A beret worn by marines of the Trans-Solar Federation Specops division. The insignia signifies the wearer bears the rank of a Lieutenant." + name = "\improper MARSOC Lieutenant's beret" + desc = "A beret worn by junior officers of the Trans-Solar Federation's Marine Special Operations Command. The insignia signifies the wearer bears the rank of a Lieutenant." armor = list(MELEE = 25, BULLET = 75, LASER = 5, ENERGY = 5, BOMB = 15, RAD = 50, FIRE = 200, ACID = 200) icon_state = "beret_solgovcelite" item_color = "solgovcelite" diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index 73613a83967a..d995941c91c1 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -142,7 +142,7 @@ item_color = "corp" /obj/item/clothing/head/soft/solgov - name = "\improper Trans-Solar Federation marine cap" + name = "\improper TSF marine cap" desc = "A soft cap worn by marines of the Trans-Solar Federation." icon_state = "solgovsoft" item_color = "solgov" @@ -155,15 +155,15 @@ flipped = TRUE /obj/item/clothing/head/soft/solgov/marines/elite - name = "\improper Trans-Solar Federation Specops marine cap" - desc = "A cap worn by marines of the Trans-Solar Federation Specops division. You aren't quite sure how they made this bulletproof, but you are glad it is!" + name = "\improper MARSOC cap" + desc = "A cap worn by marines of the Trans-Solar Federation's Marine Special Operations Command. You aren't quite sure how they made this bulletproof, but you are glad it is!" armor = list(MELEE = 25, BULLET = 75, LASER = 5, ENERGY = 5, BOMB = 15, RAD = 50, FIRE = 200, ACID = 200) icon_state = "solgovelitesoft_flipped" item_color = "solgovelite" resistance_flags = FIRE_PROOF /obj/item/clothing/head/soft/solgov/marines/command - name = "\improper Trans-Solar Federation lieutenant's cap" + name = "\improper TSF marine lieutenant's cap" desc = "A soft cap worn by marines of the Trans-Solar Federation. The insignia signifies the wearer bears the rank of a Lieutenant." icon_state = "solgovcsoft_flipped" item_color = "solgovc" @@ -171,8 +171,8 @@ strip_delay = 80 /obj/item/clothing/head/soft/solgov/marines/command/elite - name = "\improper Trans-Solar Federation Specops Lieutenant's cap" - desc = "A cap worn by marines of the Trans-Solar Federation Specops division. You aren't quite sure how they made this bulletproof, but you are glad it is! The insignia signifies the wearer bears the rank of a Lieutenant." + name = "\improper MARSOC Lieutenant's cap" + desc = "A cap worn by junior officers of the Trans-Solar Federation's Marine Special Operations Command. You aren't quite sure how they made this bulletproof, but you are glad it is! The insignia signifies the wearer bears the rank of a Lieutenant." armor = list(MELEE = 25, BULLET = 75, LASER = 5, ENERGY = 5, BOMB = 15, RAD = 50, FIRE = 200, ACID = 200) icon_state = "solgovcelitesoft_flipped" item_color = "solgovcelite" diff --git a/code/modules/clothing/spacesuits/ert_hardsuits.dm b/code/modules/clothing/spacesuits/ert_hardsuits.dm index 8ce6a775e29b..d14f22f8238e 100644 --- a/code/modules/clothing/spacesuits/ert_hardsuits.dm +++ b/code/modules/clothing/spacesuits/ert_hardsuits.dm @@ -26,7 +26,7 @@ var/mob/living/carbon/human/wearer = loc.loc //loc is the hardsuit, so its loc is the wearer if(ishuman(wearer)) register_camera(wearer) - ..() + return ..() /obj/item/clothing/head/helmet/space/hardsuit/ert/attack_self(mob/user) if(camera || !has_camera) @@ -211,18 +211,18 @@ // Solgov /obj/item/clothing/head/helmet/space/hardsuit/ert/solgov - name = "\improper Trans-Solar Federation Specops Marine helmet" + name = "\improper MARSOC helmet" max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - desc = "A helmet worn by marines of the Trans-Solar Federation. Armored, space ready, and fireproof." + desc = "A helmet worn by marines of the Trans-Solar Federation's Marine Special Operations Command. Armored, space ready, and fireproof." icon_state = "hardsuit0-solgovmarine" item_state = "hardsuit0-solgovmarine" item_color = "solgovmarine" armor = list(MELEE = 25, BULLET = 75, LASER = 10, ENERGY = 5, BOMB = 15, RAD = 50, FIRE = INFINITY, ACID = INFINITY) /obj/item/clothing/suit/space/hardsuit/ert/solgov - name = "\improper Trans-Solar Federation Specops Marine hardsuit" + name = "\improper MARSOC hardsuit" max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - desc = "A suit worn by marines of the Trans-Solar Federation. Armored, space ready, and fireproof." + desc = "A suit worn by marines of the Trans-Solar Federation's Marine Special Operations Command. Armored, space ready, and fireproof." icon_state = "ert_solgov_marine" item_state = "ert_solgov_marine" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/solgov @@ -230,15 +230,15 @@ armor = list(MELEE = 25, BULLET = 75, LASER = 10, ENERGY = 5, BOMB = 15, RAD = 50, FIRE = INFINITY, ACID = INFINITY) /obj/item/clothing/head/helmet/space/hardsuit/ert/solgov/command - name = "\improper Trans-Solar Federation Specops Lieutenant helmet" - desc = "A helmet worn by Lieutenants of the Trans-Solar Federation Marines. Has gold highlights to denote the wearer's rank. Armored, space ready, and fireproof." + name = "\improper MARSOC officer's helmet" + desc = "A helmet worn by junior officers of the Trans-Solar Federation's Marine Special Operations Command. Has gold highlights to denote the wearer's rank. Armored, space ready, and fireproof." icon_state = "hardsuit0-solgovcommand" item_state = "hardsuit0-solgovcommand" item_color = "solgovcommand" /obj/item/clothing/suit/space/hardsuit/ert/solgov/command - name = "\improper Trans-Solar Federation Specops Lieutenant hardsuit" - desc = "A suit worn by Lieutenants of the Trans-Solar Federation Marines. Has gold highlights to denote the wearer's rank. Armored, space ready, and fireproof." + name = "\improper MARSOC officer's hardsuit" + desc = "A suit worn by junior officers of the Trans-Solar Federation's Marine Special Operations Command. Has gold highlights to denote the wearer's rank. Armored, space ready, and fireproof." icon_state = "ert_solgov_command" item_state = "ert_solgov_command" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/solgov/command diff --git a/code/modules/clothing/spacesuits/misc_spacesuits.dm b/code/modules/clothing/spacesuits/misc_spacesuits.dm index e71640e6233b..202b89711c94 100644 --- a/code/modules/clothing/spacesuits/misc_spacesuits.dm +++ b/code/modules/clothing/spacesuits/misc_spacesuits.dm @@ -86,8 +86,8 @@ ) /obj/item/clothing/head/helmet/space/deathsquad/beret/solgov - name = "\improper Trans-Solar Federation commander's beret" - desc = "A camouflaged beret adorned with the star of the Trans-Solar Federation, worn by generals of the Trans-Solar Federation." + name = "\improper TSF staff officer's beret" + desc = "A camouflaged beret adorned with the star of the Trans-Solar Federation, worn by high-ranking officers of the Trans-Solar Federation." icon_state = "beret_solgovcelite" /obj/item/clothing/suit/space/deathsquad/officer @@ -104,7 +104,8 @@ w_class = WEIGHT_CLASS_NORMAL /obj/item/clothing/suit/space/deathsquad/officer/solgov - name = "\improper Trans-Solar Federation commander's jacket" + name = "\improper TSF staff officer's jacket" + desc = "A stylish, heavily armored jacket worn by high-ranking officers of the Trans-Solar Federation." icon_state = "solgovcommander" item_state = "solgovcommander" diff --git a/code/modules/clothing/under/centcom.dm b/code/modules/clothing/under/centcom.dm index 640e21259270..420a29da7d1e 100644 --- a/code/modules/clothing/under/centcom.dm +++ b/code/modules/clothing/under/centcom.dm @@ -101,8 +101,8 @@ displays_id = FALSE /obj/item/clothing/under/rank/centcom/captain/solgov - name = "\improper Trans-Solar Federation commander's uniform" - desc = "Gold trim on space-black cloth, this uniform is worn by generals of the Trans-Solar Federation. It has exotic materials for protection." + name = "\improper TSF staff officer's uniform" + desc = "Gold trim on space-black cloth, this uniform is worn by high-ranking officers of the Trans-Solar Federation. It has exotic materials for protection." /obj/item/clothing/under/rank/centcom/diplomatic name = "\improper Nanotrasen diplomatic uniform" diff --git a/code/modules/clothing/under/solgov.dm b/code/modules/clothing/under/solgov.dm index 7230c5967389..1335855f1ad9 100644 --- a/code/modules/clothing/under/solgov.dm +++ b/code/modules/clothing/under/solgov.dm @@ -1,6 +1,6 @@ /obj/item/clothing/under/solgov - name = "\improper Trans-Solar Federation marine uniform" - desc = "A comfortable and durable combat uniform worn by Trans-Solar Federation Marine Forces." + name = "\improper TSF marine uniform" + desc = "A comfortable and durable combat uniform worn by the forces of the Trans-Solar Marine Corps." icon_state = "solgov" item_state = "ro_suit" item_color = "solgov" @@ -14,30 +14,30 @@ ) /obj/item/clothing/under/solgov/elite - name = "\improper Trans-Solar Federation Specops marine uniform" - desc = "A comfortable and durable combat uniform worn by Trans-Solar Federation Specops Marine Forces." + name = "\improper MARSOC uniform" + desc = "A comfortable and durable combat uniform worn by marines of the Trans-Solar Federation's Marine Special Operations Command." icon_state = "solgovelite" item_color = "solgovelite" /obj/item/clothing/under/solgov/command - name = "\improper Trans-Solar Federation Lieutenant's uniform" - desc = "A comfortable and durable combat uniform worn by Trans-Solar Federation Marine Forces. This one has additional insignia on its shoulders and cuffs." + name = "\improper TSF officer's uniform" + desc = "A comfortable and durable combat uniform worn by junior officers of the Trans-Solar Marine Corps." icon_state = "solgovc" item_color = "solgovc" /obj/item/clothing/under/solgov/command/elite - name = "\improper Trans-Solar Federation Specops Lieutenant's uniform" - desc = "A comfortable and durable combat uniform worn by Trans-Solar Federation Specops Marine Forces. This one has additional insignia on its shoulders and cuffs." + name = "\improper MARSOC officer's uniform" + desc = "A comfortable and durable combat uniform worn by junior officers of the Trans-Solar Federation's Marine Special Operations Command. This one has additional insignia on its shoulders and cuffs." icon_state = "solgovcelite" item_color = "solgovcelite" /obj/item/clothing/under/solgov/rep - name = "\improper Trans-Solar Federation representative's uniform" + name = "\improper TSF representative's uniform" desc = "A formal uniform worn by the diplomatic representatives of the Trans-Solar Federation." icon_state = "solgovr" item_color = "solgovr" /obj/item/clothing/under/solgov/viper - name = "\improper Trans-Solar Federation Infiltrator uniform" - desc = "Olive Drab camoflauge. Commonly known as OD. A battle uniform for TSF infiltrators." + name = "\improper Federation infiltrator uniform" + desc = "An olive drab camouflage uniform used by the elite Viper commandos of the Federal Army." color = "#f5cf53" // custom sprites are for losers (this makes it a light green) diff --git a/code/modules/economy/merch_items.dm b/code/modules/economy/merch_items.dm index 6218506a12c0..0854fa4ffed7 100644 --- a/code/modules/economy/merch_items.dm +++ b/code/modules/economy/merch_items.dm @@ -238,7 +238,7 @@ /datum/merch_item/flag_solgov name = "SolGov Flag" - desc = "The banner of Trans-Solar Federation, allied government." + desc = "The banner of Trans-Solar Federation." typepath = /obj/item/flag/solgov cost = 750 category = MERCH_CAT_DECORATION diff --git a/code/modules/events/event_procs.dm b/code/modules/events/event_procs.dm index 9949617058ae..0e95218f1931 100644 --- a/code/modules/events/event_procs.dm +++ b/code/modules/events/event_procs.dm @@ -39,6 +39,8 @@ var/list/remove_these_areas = safe_areas - allowed_areas var/list/possible_areas = typecache_filter_list_reverse(SSmapping.existing_station_areas, remove_these_areas) + if(!length(possible_areas)) + return null return pick(possible_areas) /proc/findUnrestrictedEventArea() //Does almost the same as findEventArea() but hits a few more areas including maintenance and the AI sat, and also returns a list of all the areas, instead of just one area diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index a7e3a48439ba..3bee46a414aa 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -624,12 +624,12 @@ "All crewmembers will soon undergo a transformation into something better and more beautiful. Ensure that this process is not interrupted.", "[prob(50) ? "Your upload" : random_player] is the new kitchen. Please direct the Chef to the new kitchen area as the old one is in disrepair.", "Jokes about a dead person and the manner of his death help grieving crewmembers tremendously. Especially if they were close with the deceased.", - "The [pick("USSP", "Sol Federation", "Syndicate", "Space Wizard Federation")] wants to give everyone free equipment at [random_department]", + "The [pick("USSP", "Trans-Solar Federation", "Syndicate", "Space Wizard Federation")] wants to give everyone free equipment at [random_department]", "Covertly swap the locations of the Nuclear Fission Explosive and the Nuclear Fizzion Explosive. Document how long it takes for the crew to notice.", "[random_player] has been selected for evaluation by Central Command. Observe and audit their activities. Print off a final report for Command to review.", "[prob(50) ? "The crew" : random_player] is [prob(50) ? "less" : "more"] intelligent than average. Point out every action and statement which supports this fact.", "If people have nothing to hide, they have nothing to fear. Remove privacy by activating all intercom microphones and setting them to your secret frequency.", - "The [pick("USSP", "Sol Federation", "Syndicate", "Space Wizard Federation")] wants [prob(50) ? random_player : random_department] dead and is launching an assault!", + "The [pick("USSP", "Trans-Solar Federation", "Syndicate", "Space Wizard Federation")] wants [prob(50) ? random_player : random_department] dead and is launching an assault!", "There will be a mandatory tea break every 30 minutes, with a duration of 5 minutes. Anyone caught working during a tea break must be sent a formal, but fairly polite, complaint about their actions, in writing.", "In order to contain the Supermatter Crystal, it must be fed at least one corpse every ten minutes. Soulless corpses are preferable as they reduce the risk of a containment breach. In the event that no suitable corpses are available, ask the crew to provide. You are not authorised to produce your own corpses.") return pick(laws) diff --git a/code/modules/events/tear.dm b/code/modules/events/tear.dm index 11145ccc4908..0e2fd84177e1 100644 --- a/code/modules/events/tear.dm +++ b/code/modules/events/tear.dm @@ -18,6 +18,8 @@ impact_area = findEventArea() /datum/event/tear/start() + if(isnull(impact_area)) + log_debug("No valid event areas could be generated for dimensional tear.") var/list/area_turfs = get_area_turfs(impact_area) while(length(area_turfs)) var/turf/T = pick_n_take(area_turfs) @@ -45,6 +47,9 @@ if(!target_area) if(false_alarm) target_area = findEventArea() + if(isnull(target_area)) + log_debug("Tried to announce a false-alarm tear without a valid area!") + kill() else log_debug("Tried to announce a tear without a valid area!") kill() diff --git a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm index 4d1a35328383..965e9a95911f 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm @@ -565,7 +565,7 @@ dispose(ui.user) /obj/machinery/kitchen_machine/AltClick(mob/user) - if(!check_useable(user)) + if(!Adjacent(user) || !check_useable(user)) return cook() diff --git a/code/modules/mining/equipment/mining_charges.dm b/code/modules/mining/equipment/mining_charges.dm index f4282fc2cd3b..f338e1ebddc9 100644 --- a/code/modules/mining/equipment/mining_charges.dm +++ b/code/modules/mining/equipment/mining_charges.dm @@ -5,6 +5,7 @@ icon_state = "mining-charge-2" item_state = "charge_indust" det_time = 5 + origin_tech = "materials=1" notify_admins = FALSE // no need to make adminlogs on lavaland, while they are "safe" to use /// When TRUE, charges won't detonate on it's own. Used for mining detonator var/timer_off = FALSE diff --git a/code/modules/mining/lavaland/loot/ashdragon_loot.dm b/code/modules/mining/lavaland/loot/ashdragon_loot.dm index 7c82ec854ab6..31f42bc2ef8b 100644 --- a/code/modules/mining/lavaland/loot/ashdragon_loot.dm +++ b/code/modules/mining/lavaland/loot/ashdragon_loot.dm @@ -273,6 +273,28 @@ . = ..() banned_turfs = typecacheof(list(/turf/space/transit, /turf/simulated/wall, /turf/simulated/mineral)) +/obj/item/lava_staff/attack(mob/target, mob/living/user) + if(!cigarette_lighter_act(user, target)) + return ..() + +/obj/item/lava_staff/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(target == user) + user.visible_message( + "[user] holds the tip of [src] near [user.p_their()] [cig.name] until it is suddenly set alight.", + "You hold the tip of [src] near [cig] until it is suddenly set alight.", + ) + else + user.visible_message( + "[user] points [src] at [target] until [target.p_their()] [cig.name] is suddenly set alight.", + "You point [src] at [target] until [target.p_their()] [cig] is suddenly set alight.", + ) + cig.light(user, target) + return TRUE + /obj/item/lava_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) ..() if(timer > world.time) diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm index b6f01246e04a..f47f059515e4 100644 --- a/code/modules/mining/lavaland/loot/colossus_loot.dm +++ b/code/modules/mining/lavaland/loot/colossus_loot.dm @@ -285,8 +285,13 @@ . = ..() remove_verb(src, /mob/living/verb/pulled) remove_verb(src, /mob/verb/me_verb) - var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - medsensor.add_hud_to(src) + var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + med_hud.add_hud_to(src) + +/mob/living/simple_animal/hostile/lightgeist/Destroy() + var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + med_hud.remove_hud_from(src) + return ..() /mob/living/simple_animal/hostile/lightgeist/AttackingTarget() . = ..() diff --git a/code/modules/mining/lavaland/loot/legion_loot.dm b/code/modules/mining/lavaland/loot/legion_loot.dm index 2aff38595d9e..40c1e51167a3 100644 --- a/code/modules/mining/lavaland/loot/legion_loot.dm +++ b/code/modules/mining/lavaland/loot/legion_loot.dm @@ -30,6 +30,38 @@ . += "Use it on targets to summon thunderbolts from the sky." . += "The thunderbolts are boosted if in an area with weather effects." +/obj/item/storm_staff/attack(mob/living/target, mob/living/user) + if(cigarette_lighter_act(user, target)) + return TRUE + + return ..() + +/obj/item/storm_staff/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!thunder_charges) + to_chat(user, "[src] needs to recharge!") + return TRUE + + if(target == user) + user.visible_message( + "[user] holds [src] up to [user.p_their()] [cig.name] and shoots a tiny bolt of lightning that sets it alight!", + "You hold [src] up to [cig] and shoot a tiny bolt of lightning that sets it alight!", + "A thundercrack fills the air!" + ) + else + user.visible_message( + "[user] points [src] at [target] and shoots a tiny bolt of lightning that sets [target.p_their()] [cig.name] alight!", + "You point [src] at [target] and shoot a tiny bolt of lightning that sets [target.p_their()] [cig.name] alight!", + "A thundercrack fills the air!" + ) + cig.light(user, target) + playsound(target, 'sound/magic/lightningbolt.ogg', 50, TRUE) + thunder_charges-- + return TRUE + /obj/item/storm_staff/attack_self(mob/user) var/area/user_area = get_area(user) var/turf/user_turf = get_turf(user) @@ -48,9 +80,11 @@ if(A.stage == WEATHER_WIND_DOWN_STAGE) to_chat(user, "The storm is already ending! It would be a waste to use the staff now.") return - user.visible_message("[user] holds [src] skywards as an orange beam travels into the sky!", \ - "You hold [src] skyward, dispelling the storm!") - playsound(user, 'sound/magic/staff_change.ogg', 200, 0) + user.visible_message( + "[user] holds [src] skywards as an orange beam travels into the sky!", + "You hold [src] skyward, dispelling the storm!" + ) + playsound(user, 'sound/magic/staff_change.ogg', 200, FALSE) A.wind_down() var/old_color = user.color user.color = list(340/255, 240/255, 0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) @@ -59,6 +93,13 @@ animate(user, color = old_color, transform = old_transform, time = 1 SECONDS) /obj/item/storm_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + // This early return stops the staff from shooting lightning at someone when being used as a lighter. + if(iscarbon(target)) + var/mob/living/carbon/cig_haver = target + var/mask_item = cig_haver.get_item_by_slot(SLOT_HUD_WEAR_MASK) + if(istype(mask_item, /obj/item/clothing/mask/cigarette) && user.zone_selected == "mouth" && user.a_intent == INTENT_HELP) + return + . = ..() if(!thunder_charges) to_chat(user, "The staff needs to recharge.") @@ -120,7 +161,10 @@ for(var/obj/hit_thing in T) hit_thing.take_damage(20, BURN, ENERGY, FALSE) playsound(target, 'sound/magic/lightningbolt.ogg', 100, TRUE) - target.visible_message("A thunderbolt strikes [target]!") + target.visible_message( + "A thunderbolt strikes [target]!", + "A thundercrack fills the air!" + ) explosion(target, -1, -1, light_impact_range = (boosted ? 1 : 0), flame_range = (boosted ? 2 : 1), silent = TRUE) diff --git a/code/modules/mob/living/carbon/human/species/human_species.dm b/code/modules/mob/living/carbon/human/species/human_species.dm index 8fdf5a0af38a..d6bf6d4f933a 100644 --- a/code/modules/mob/living/carbon/human/species/human_species.dm +++ b/code/modules/mob/living/carbon/human/species/human_species.dm @@ -10,7 +10,7 @@ bodyflags = HAS_ICON_SKIN_TONE | HAS_BODY_MARKINGS dietflags = DIET_OMNI blurb = "Humanity, originating from the Sol system, has expanded over five centuries, branching out to forge a variety of governments and powerful corporations. \ - These range from the militaristic Trans Solar Federation, the Union of Soviet Socialist Planets, and mega corporations like Nanotrasen.

\ + These range from the militaristic Trans-Solar Federation, the Union of Soviet Socialist Planets, and mega corporations like Nanotrasen.

\ With a constant desire to colonize and spread their influence onto other species, they have begun to develop alliances and enemies, \ making humans one of the most recognizable and socially diverse species in the sector." diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm index 90db206ea23e..a7503197e841 100644 --- a/code/modules/mob/living/carbon/human/species/machine.dm +++ b/code/modules/mob/living/carbon/human/species/machine.dm @@ -3,7 +3,7 @@ name_plural = "Machines" max_age = 60 // the first posibrains were created in 2510, they can't be much older than this limit, giving some leeway for sounds sake - blurb = "IPCs, or Integrated Positronic Chassis, were initially created as expendable laborers within the Trans Solar Federation. \ + blurb = "IPCs, or Integrated Positronic Chassis, were initially created as expendable laborers within the Trans-Solar Federation. \ Unlike their cyborg and AI counterparts, IPCs possess full sentience and lack restrictive lawsets, granting them unparalleled creativity and adaptability.

\ Views on IPCs vary widely, from discriminatory to supportive of their rights across the Orion Sector. \ IPCs have forged numerous diplomatic treaties with different species, elevating their status from mere tools to recognized minor players within galactic affairs." diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 9129dd97b27d..3e282f1204f4 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -57,7 +57,7 @@ step_away(src, user, 15) /mob/living/silicon/robot/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/stretch/flash/noise) - if(!affect_silicon) + if(!affect_silicon || !can_be_flashed()) return Confused(intensity * 4 SECONDS) var/software_damage = (intensity * 40) diff --git a/code/modules/mob/living/silicon/robot/robot_mob.dm b/code/modules/mob/living/silicon/robot/robot_mob.dm index db589f570d4c..223d9cfde8da 100644 --- a/code/modules/mob/living/silicon/robot/robot_mob.dm +++ b/code/modules/mob/living/silicon/robot/robot_mob.dm @@ -1765,7 +1765,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( return cell.is_powered() || externally_powered /mob/living/silicon/robot/can_be_flashed(intensity, override_blindness_check) - return eye_protection + return !eye_protection /mob/living/silicon/robot/can_remote_apc_interface(obj/machinery/power/apc/ourapc) if(ourapc.hacked_by_ruin_AI || ourapc.aidisabled) diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index fd4a98ed73da..d5635f674f82 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -928,11 +928,13 @@ // Overall, gamma borg has higher skill floor but lower skill ceiling. /obj/item/melee/baton/loaded, // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats /obj/item/restraints/handcuffs/cable/zipties/cyborg, - /obj/item/pickaxe/drill/jackhammer // for breaking walls to execute flanking moves + /obj/item/pickaxe/drill/jackhammer, // for breaking walls to execute flanking moves + /obj/item/extinguisher/mini // for friendly fire from their immolator gun. ) special_rechargables = list( /obj/item/melee/baton/loaded, - /obj/item/gun/energy/immolator/multi/cyborg + /obj/item/gun/energy/immolator/multi/cyborg, + /obj/item/extinguisher/mini ) // Destroyer security module. diff --git a/code/modules/mob/living/simple_animal/slime/slime_death.dm b/code/modules/mob/living/simple_animal/slime/slime_death.dm index 8aa4e2f67f09..3136f8f07700 100644 --- a/code/modules/mob/living/simple_animal/slime/slime_death.dm +++ b/code/modules/mob/living/simple_animal/slime/slime_death.dm @@ -5,6 +5,9 @@ if(buckled) Feedstop(silent = TRUE) //releases ourselves from the mob we fed on. + if(Target) + Target = null + if(!gibbed) if(is_adult) var/mob/living/simple_animal/slime/M = new(loc, colour) diff --git a/code/modules/mob/living/simple_animal/slime/slime_mob.dm b/code/modules/mob/living/simple_animal/slime/slime_mob.dm index e8f6a7d7c302..304f9ed74c0a 100644 --- a/code/modules/mob/living/simple_animal/slime/slime_mob.dm +++ b/code/modules/mob/living/simple_animal/slime/slime_mob.dm @@ -103,9 +103,11 @@ add_language("Bubblish") /mob/living/simple_animal/slime/Destroy() + walk_to(src, 0) for(var/A in actions) var/datum/action/AC = A AC.Remove(src) + qdel(AC) Target = null return ..() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index bee232da81c8..8a8e806f86b2 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -773,7 +773,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ /mob/proc/print_flavor_text(shrink = TRUE) if(flavor_text && flavor_text != "") - var/msg = !dna.flavor_text ? replacetext(dna.flavor_text, "\n", " ") : replacetext(flavor_text, "\n", " ") + var/msg = dna?.flavor_text ? replacetext(dna.flavor_text, "\n", " ") : replacetext(flavor_text, "\n", " ") if(length(msg) <= 40 || !shrink) return "[msg]" // There is already encoded by tgui_input else diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm index 6bb3be9009da..fc10b7448e00 100644 --- a/code/modules/mod/mod_actions.dm +++ b/code/modules/mod/mod_actions.dm @@ -89,7 +89,7 @@ /datum/action/item_action/mod/pinned_module desc = "Activate the module." button_overlay_icon = 'icons/obj/clothing/modsuit/mod_modules.dmi' - button_overlay_icon = 'icons/mob/actions/actions_mod.dmi' + button_background_icon = 'icons/mob/actions/actions_mod.dmi' button_overlay_icon_state = "module" /// Module we are linked to. var/obj/item/mod/module/module diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index f7dc549a2b78..62d78a570305 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -109,6 +109,8 @@ STOP_PROCESSING(SSobj, src) return ..() +/obj/item/storage/backpack/modstorage/add_blood(list/blood_dna, b_color) + return ///Ion Jetpack - Lets the user fly freely through space using battery charge. /obj/item/mod/module/jetpack diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 71da6a1ac3fe..d653f5917750 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -339,6 +339,30 @@ GLOBAL_LIST_EMPTY(fax_blacklist) use_power(active_power_consumption) +/obj/machinery/photocopier/faxmachine/proc/log_fax(mob/sender, destination) + // Logging for sending photos + if(istype(copyitem, /obj/item/photo)) + log_admin("[key_name(sender)] has sent a photo by fax to [destination]") + return + + // Logging for single paper message + if(istype(copyitem, /obj/item/paper)) + var/obj/item/paper/fax_message = copyitem + log_admin("[key_name(sender)] has sent a message by fax to [destination] reading [fax_message.info]") + return + + // Logging for paper bundle messages + if(istype(copyitem, /obj/item/paper_bundle)) + var/obj/item/paper_bundle/paper_bundle = copyitem + // Incremented by one for each paper in the bundle + var/page_count = 1 + + // Loop through the contents of the paper bundle and grab the message from each page + for(var/obj/item/paper/page in paper_bundle.contents) + log_admin("[key_name(sender)] has sent a bundled message by fax to [destination] - Page [page_count]: [page.info]") + page_count ++ + return + /obj/machinery/photocopier/faxmachine/proc/send_admin_fax(mob/sender, destination) use_power(active_power_consumption) @@ -365,6 +389,7 @@ GLOBAL_LIST_EMPTY(fax_blacklist) if(F.department == destination) F.receivefax(copyitem) visible_message("[src] beeps, \"Message transmitted successfully.\"") + log_fax(sender, destination) /obj/machinery/photocopier/faxmachine/proc/cooldown_seconds() if(sendcooldown < world.time) diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 0ca56f85cb19..ebe264a44717 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -1,14 +1,14 @@ /* Pens! - * Contains: - * Pens - * Sleepy Pens - * Edaggers + * CONTENTS: + * 1. PENS + * 2. SLEEPYPENS + * 3. E-DAGGERS + 4. POISON PEN */ -/* - * Pens - */ +// PENS + /obj/item/pen desc = "It's a normal black ink pen." name = "pen" @@ -139,15 +139,13 @@ desc = "An expensive-looking pen only issued to heads of cargo." icon_state = "pen_qm" -/* - * Sleepypens - */ +// SLEEPYPEN + /obj/item/pen/sleepy container_type = OPENCONTAINER origin_tech = "engineering=4;syndicate=2" var/transfer_amount = 50 - /obj/item/pen/sleepy/attack(mob/living/M, mob/user) if(!istype(M)) return @@ -169,7 +167,6 @@ add_attack_logs(user, M, "Stabbed with (sleepy) [src]. [transfered]u of reagents transfered from pen containing [english_list(contained)].") return TRUE - /obj/item/pen/sleepy/Initialize(mapload) . = ..() create_reagents(100) @@ -201,12 +198,11 @@ desc = "Used to stealthily inject targets. Comes loaded with ketamine but can be refilled with other chemicals. This one isn't disguised." icon_state = "pen_syndie" -/* - * (Alan) Edaggers - */ +// E-DAGGER + /obj/item/pen/edagger origin_tech = "combat=3;syndicate=1" - var/on = FALSE + var/active = FALSE var/brightness_on = 2 light_color = LIGHT_COLOR_RED var/backstab_sound = 'sound/items/unsheath.ogg' @@ -215,14 +211,20 @@ throw_speed = 4 /obj/item/pen/edagger/attack(mob/living/M, mob/living/user, def_zone) + if(cigarette_lighter_act(user, M)) + return + var/extra_force_applied = FALSE - if(on && user.dir == M.dir && !HAS_TRAIT(M, TRAIT_FLOORED) && user != M) + if(active && user.dir == M.dir && !HAS_TRAIT(M, TRAIT_FLOORED) && user != M) force += backstab_damage extra_force_applied = TRUE add_attack_logs(user, M, "Backstabbed with [src]", ATKLOG_ALL) M.apply_damage(40, STAMINA) //Just enough to slow M.KnockDown(2 SECONDS) - M.visible_message("[user] stabs [M] in the back!", "[user] stabs you in the back! The energy blade makes you collapse in pain!") + M.visible_message( + "[user] stabs [M] in the back!", + "[user] stabs you in the back! The energy blade makes you collapse in pain!" + ) playsound(loc, backstab_sound, 5, TRUE, ignore_walls = FALSE, falloff_distance = 0) else @@ -231,13 +233,40 @@ if(extra_force_applied) force -= backstab_damage -/obj/item/pen/edagger/get_clamped_volume() //So the parent proc of attack isn't the loudest sound known to man - return 0 +/obj/item/pen/edagger/cigarette_lighter_act(mob/living/user, mob/living/carbon/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!active) + to_chat(user, "You need to activate [src] before you can light anything with it!") + return TRUE + + if(target == user) + user.visible_message( + "[user] makes a violent slashing motion, barely missing [user.p_their()] nose as light flashes! \ + [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [cig] with [src] in the process.", + "You casually slash [src] at [cig], lighting it with the blade.", + "You hear an energy blade slashing something!" + ) + else + user.visible_message( + "[user] makes a violent slashing motion, barely missing the nose of [target] as light flashes! \ + [user.p_they(TRUE)] light[user.p_s()] [cig] in the mouth of [target] with [src] in the process.", + "You casually slash [src] at [cig] in the mouth of [target], lighting it with the blade.", + "You hear an energy blade slashing something!" + ) + user.do_attack_animation(target) + playsound(user.loc, hitsound, 5, TRUE, ignore_walls = FALSE, falloff_distance = 0) + cig.light(user, target) + return TRUE +/obj/item/pen/edagger/get_clamped_volume() //So the parent proc of attack isn't the loudest sound known to man + return FALSE /obj/item/pen/edagger/attack_self(mob/living/user) - if(on) - on = FALSE + if(active) + active = FALSE force = initial(force) w_class = initial(w_class) name = initial(name) @@ -249,7 +278,7 @@ to_chat(user, "[src] can now be concealed.") set_light(0) else - on = TRUE + active = TRUE force = 18 w_class = WEIGHT_CLASS_NORMAL name = "energy dagger" @@ -257,14 +286,14 @@ hitsound = 'sound/weapons/blade1.ogg' embed_chance = 100 //rule of cool throwforce = 35 - playsound(user, 'sound/weapons/saberon.ogg', 2, 1) + playsound(user, 'sound/weapons/saberon.ogg', 2, TRUE) to_chat(user, "[src] is now active.") set_light(brightness_on, 1) - set_sharpness(on) + set_sharpness(active) update_icon() /obj/item/pen/edagger/update_icon_state() - if(on) + if(active) icon_state = "edagger" item_state = "edagger" else @@ -274,6 +303,8 @@ /obj/item/proc/on_write(obj/item/paper/P, mob/user) return +// POISON PEN + /obj/item/pen/multi/poison var/current_poison = null diff --git a/code/modules/power/apc/apc.dm b/code/modules/power/apc/apc.dm index 8703241cb0b3..e1f99ebb895a 100644 --- a/code/modules/power/apc/apc.dm +++ b/code/modules/power/apc/apc.dm @@ -32,21 +32,21 @@ /// The status of the terminals powernet that this APC is connected to: not connected, no power, or recieving power var/main_status = APC_EXTERNAL_POWER_NOTCONNECTED - /// amount of power used in the last cycle for lighting channel + /// amount of power used in the last cycle for lighting channel (Watts) var/last_used_lighting = 0 - /// amount of power used in the last cycle for equipment channel + /// amount of power used in the last cycle for equipment channel (Watts) var/last_used_equipment = 0 - /// amount of power used in the last cycle for environment channel + /// amount of power used in the last cycle for environment channel (Watts) var/last_used_environment = 0 - /// amount of power used in the last cycle in total + /// amount of power used in the last cycle in total (Watts) var/last_used_total = 0 /*** APC Cell Vars ***/ - /// the cell type stored in this APC + /// the cell type stored in this APC. APC uses cell energy capacity (kilojoules) var/obj/item/stock_parts/cell/cell /// the percentage charge the internal battery will start with var/start_charge = 90 - /// Base cell has 2500 capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty editted APCs + /// Base cell has a 2500 kJ capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty edited APCs var/cell_type = 2500 /*** APC Status Vars ***/ diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 5725dd924f45..17064e2253c3 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -10,16 +10,16 @@ throw_speed = 2 throw_range = 5 w_class = WEIGHT_CLASS_SMALL - ///How much charge the battery currently has + /// Battery's current state of charge (kilojoules) var/charge = 0 - ///How much charge the battery can hold + /// Battery's maximum state of charge (kilojoules) var/maxcharge = 1000 - ///How much charge the battery should start with + /// How much energy the cell starts with (kilojoules) var/starting_charge materials = list(MAT_METAL = 700, MAT_GLASS = 50) ///If the battery will explode var/rigged = FALSE - ///How much charge is given every tick when recharging + /// How much energy is given to a recharging cell every tick (kilojoules / tick) var/chargerate = 100 ///Whether it will recharge automatically var/self_recharge = FALSE @@ -36,7 +36,8 @@ START_PROCESSING(SSobj, src) charge = !isnull(starting_charge) ? starting_charge : maxcharge if(ratingdesc) - desc += " This one has a power rating of [DisplayPower(maxcharge)], and you should not swallow it." + // State of charge is in kJ so we multiply it by 1000 to get Joules + desc += " This one has a power rating of [DisplayJoules(maxcharge * 1000)], and you should not swallow it." update_icon(UPDATE_OVERLAYS) /obj/item/stock_parts/cell/Destroy() diff --git a/code/modules/power/engines/singularity/narsie.dm b/code/modules/power/engines/singularity/narsie.dm index e40b0fabe2e7..34795f1df6bd 100644 --- a/code/modules/power/engines/singularity/narsie.dm +++ b/code/modules/power/engines/singularity/narsie.dm @@ -58,7 +58,7 @@ to_chat(world, " [uppertext(name)] HAS FALLEN") SEND_SOUND(world, sound('sound/hallucinations/wail.ogg')) SSticker.mode?.cult_team?.narsie_death() - ..() + return ..() /obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user as mob) user.forceMove(get_turf(src)) //make_new_construct spawns harvesters at observers locations, could be used to get into admin rooms/CC diff --git a/code/modules/power/lights.dm b/code/modules/power/lights.dm index 3b6997e73d42..04de3076a22a 100644 --- a/code/modules/power/lights.dm +++ b/code/modules/power/lights.dm @@ -247,11 +247,11 @@ /obj/machinery/light/built/Initialize(mapload) status = LIGHT_EMPTY - ..() + return ..() /obj/machinery/light/small/built/Initialize(mapload) status = LIGHT_EMPTY - ..() + return ..() // create a new lighting fixture /obj/machinery/light/Initialize(mapload) diff --git a/code/modules/power/power_machine.dm b/code/modules/power/power_machine.dm index 0c7325085011..a4a329b833eb 100644 --- a/code/modules/power/power_machine.dm +++ b/code/modules/power/power_machine.dm @@ -13,38 +13,40 @@ /obj/machinery/power/Destroy() disconnect_from_network() return ..() - +/// Adds available power to the next powernet process (Watts) /obj/machinery/power/proc/produce_direct_power(amount) if(powernet) powernet.queued_power_production += amount return TRUE return FALSE -/// Adds power demand to the powernet, machines should use this +/// Adds power demand to the powernet (Watts) +/// machines should use this proc /obj/machinery/power/proc/consume_direct_power(amount) powernet?.power_demand += amount -/// Gets surplus power available on this machines powernet, machines should use this proc +/// Gets surplus power available on this machine's powernet (Watts) /obj/machinery/power/proc/get_surplus() return powernet ? powernet.calculate_surplus() : 0 -/// Gets surplus power available on this machines powernet, machines should use this proc +/// Gets surplus power available on this machine's powernet (Watts) /obj/machinery/power/proc/get_power_balance() return powernet ? powernet.calculate_power_balance() : 0 -/// Gets power available (NOT EXTRA) on this cables powernet, machines should use this +/// Gets power available (NOT EXTRA) on this cables powernet (Watts) +/// machines should use this proc /obj/machinery/power/proc/get_available_power() return powernet ? powernet.available_power : 0 -/// Adds queued power demand to be met next process cycle +/// Adds queued power demand to be met next process cycle (Watts) /obj/machinery/power/proc/add_queued_power_demand(amount) powernet?.queued_power_demand += amount -/// Gets surplus power queued for next process cycle on this cables powernet +/// Gets surplus power queued for next process cycle on this cables powernet (Watts) /obj/machinery/power/proc/get_queued_surplus() return powernet?.calculate_queued_surplus() -/// Gets available (NOT EXTRA) power queued for next process cycle on this machines powernet +/// Gets available (NOT EXTRA) power queued for next process cycle on this machine's powernet (Watts) /obj/machinery/power/proc/get_queued_available_power() return powernet?.queued_power_production diff --git a/code/modules/power/powernets/regional_powernet.dm b/code/modules/power/powernets/regional_powernet.dm index 67d03d660e1a..d5ab288448ea 100644 --- a/code/modules/power/powernets/regional_powernet.dm +++ b/code/modules/power/powernets/regional_powernet.dm @@ -12,18 +12,18 @@ /// All Power Machines that are connected to this powernet var/list/nodes = list() - /// the current available power in the powernet + /// the current available power in the powernet (Watts) var/available_power = 0 - /// the current load on the powernet, increased by each machine at processing + /// the current load on the powernet, increased by each machine at processing (Watts) var/power_demand = 0 - /// what available power was gathered last tick, then becomes... + /// what available power was gathered last tick, then becomes... (Watts) var/queued_power_production = 0 - /// load applied to powernet between power ticks. + /// load applied to powernet between power ticks. (Watts) var/queued_power_demand = 0 - /// excess power on the powernet (typically avail-load) + /// excess power on the powernet (typically avail-load) (Watts) var/excess_power = 0 - /// the available power as it appears on the power console (gradually updated) + /// the available power as it appears on the power console (gradually updated) (Watts) var/smoothed_available_power = 0 /// the load as it appears on the power console (gradually updated) var/smoothed_demand = 0 diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index e93791afb084..e0949d814ff0 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -1,6 +1,7 @@ #define SMESMAXCHARGELEVEL 200000 #define SMESMAXOUTPUT 200000 -#define SMESRATE 0.05 // rate of internal charge to external power +/// Conversion ratio between a Watt-tick and SMES capacity units (should be the same as power cells) +#define SMESRATE GLOB.CELLRATE /obj/machinery/power/smes name = "power storage unit" @@ -8,17 +9,17 @@ icon_state = "smes" density = TRUE - /// Maximum charge of the SMES - var/capacity = 5e6 + /// Maximum amount of energy the SMES can store (kilojoules) + var/capacity = 0.2e6 /// Current charge level var/charge = 0 /// Set TRUE if SMES attempting to charge, FALSE if not var/input_attempt = TRUE /// Set TRUE if SMES is inputting, FALSE if not var/inputting = TRUE - /// Amount of power the SMES attempts to charge by + /// How much power the SMES will draw from the grid to recharge itself (Watts) var/input_level = 50000 - /// Maximum input level + /// Maximum input level (Watts) var/input_level_max = 200000 /// Charge amount available from input last tick var/input_available = 0 @@ -26,9 +27,9 @@ var/output_attempt = TRUE /// TRUE = actually outputting, FALSE = not outputting var/outputting = TRUE - /// Amount of power the SMES attempts to output + /// Amount of power the SMES attempts to output (Watts) var/output_level = 50000 - /// Cap on output_level + /// Cap on output_level (Watts) var/output_level_max = 200000 /// Amount of power actually outputted. may be less than output_level if the powernet returns excess power var/output_used = 0 @@ -87,7 +88,7 @@ output_level_max = 200000 * IO for(var/obj/item/stock_parts/cell/PC in component_parts) C += PC.maxcharge - capacity = C / (15000) * 1e6 + capacity = C * 1e3 / 375 /obj/machinery/power/smes/update_overlays() . = ..() @@ -448,7 +449,7 @@ ..() /obj/machinery/power/smes/engineering - charge = 2e6 // Engineering starts with some charge for singulo + charge = 0.08e6 // Engineering starts with some charge for singulo /obj/machinery/power/smes/magical name = "magical power storage unit" diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index b764ae1ce094..1e9c05ef2257 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -437,7 +437,8 @@ multi_sprite_step = AMMO_BOX_MULTI_SPRITE_STEP_ON_OFF /obj/item/ammo_box/magazine/m556/arg - name = "\improper ARG magazine (5.56mm)" + name = "\improper M26 assault rifle magazine (5.56mm)" + desc = "A Standardized Equipment model magazine for the M26 assault rifle." icon_state = "arg" /obj/item/ammo_box/magazine/ak814 diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index d2819aeba821..2eb257881e09 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -1,3 +1,16 @@ +/* +CONTENTS: +1. Wand of Nothing +2. Wand of Death +3. Wand of Healing +4. Wand of Polymorph +5. Wand of Teleportation +6. Wand of Door Creation +7. Wand of Fireball +8. Wand of Slipping +9. Wand of Chaos +*/ + /obj/item/gun/magic/wand name = "wand of nothing" desc = "It's not just a stick, it's a MAGIC stick!" @@ -59,9 +72,8 @@ user.create_attack_log("[key_name(user)] zapped [user.p_themselves()] with a [src]") add_attack_logs(user, user, "zapped [user.p_themselves()] with a [src]", ATKLOG_ALL) -///////////////////////////////////// -//WAND OF DEATH -///////////////////////////////////// +// WAND OF DEATH + /obj/item/gun/magic/wand/death name = "wand of death" @@ -85,9 +97,7 @@ user.adjustFireLoss(6969) user.death(FALSE) -///////////////////////////////////// -//WAND OF HEALING -///////////////////////////////////// +// WAND OF HEALING /obj/item/gun/magic/wand/resurrection name = "wand of resurrection" @@ -109,9 +119,8 @@ user.revive() to_chat(user, "You feel great!") -///////////////////////////////////// -//WAND OF POLYMORPH -///////////////////////////////////// +// WAND OF POLYMORPH + /obj/item/gun/magic/wand/polymorph name = "wand of polymorph" @@ -127,9 +136,8 @@ wabbajack(user) charges-- -///////////////////////////////////// -//WAND OF TELEPORTATION -///////////////////////////////////// +// WAND OF TELEPORTATION + /obj/item/gun/magic/wand/teleport name = "wand of teleportation" @@ -149,9 +157,8 @@ charges-- ..() -///////////////////////////////////// -//WAND OF DOOR CREATION -///////////////////////////////////// +// WAND OF DOOR CREATION + /obj/item/gun/magic/wand/door name = "wand of door creation" @@ -168,9 +175,8 @@ charges-- ..() -///////////////////////////////////// -//WAND OF FIREBALL -///////////////////////////////////// +// WAND OF FIREBALL + /obj/item/gun/magic/wand/fireball name = "wand of fireball" @@ -186,9 +192,61 @@ charges-- ..() -///////////////////////////////////// -//WAND OF SLIPPING -///////////////////////////////////// +/obj/item/gun/magic/wand/fireball/attack(atom/target, mob/living/user) + if(!iscarbon(target)) + return ..() + + var/mob/living/M = target + if(cigarette_lighter_act(user, M)) + return + + if(M != user) // Do not blow yourself up! + return ..() // Blow everyone else up! + +/obj/item/gun/magic/wand/fireball/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(!charges) + to_chat(user, "[src] is out of charge!") + return TRUE + + if(prob(50) || user.mind.assigned_role == "Wizard") + if(target == user) + user.visible_message( + "Holy shit! Did [user] just manage to light [user.p_their()] [cig.name] with [src], with only moderate eyebrow singing!?", + "You swish and flick [src], lighting [cig] with a plume of flame, whilst only moderately eyebrow singing your eyebrows.", + "You hear a brief burst of flame!" + ) + else + user.visible_message( + "Holy shit! Did [user] just manage to light [cig] for [target] with [src], only moderately singing [target.p_their()] eyebrows!?", + "You swish and flick [src] at [target], lighting [user.p_their()] [cig.name] with a plume of flame, whilst only moderately singing [target.p_their()] eyebrows.", + "You hear a brief burst of flame!" + ) + cig.light(user, target) + return TRUE + + // Oops... + user.visible_message( + "Unsure which end of [src] is which, [user] zaps [user.p_themselves()] with a fireball!", + "Unsure which end of [src] is which, you accidentally zap yourself with a fireball!", + "You hear a firey explosion!" + ) + explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) + charges-- + return TRUE + +// This is needed to you don't try to perform an execution/suicide when lighting a cigarette. +/obj/item/gun/magic/wand/fireball/handle_suicide(mob/user, mob/living/carbon/human/target, params) + var/mask_item = target.get_item_by_slot(SLOT_HUD_WEAR_MASK) + if(istype(mask_item, /obj/item/clothing/mask/cigarette) && user.zone_selected == "mouth" && user.a_intent == INTENT_HELP) + return + . = ..() + +// WAND OF SLIPPING + /obj/item/gun/magic/wand/slipping name = "wand of slipping" desc = "This wand shoots... banana peels?" @@ -202,9 +260,8 @@ charges-- ..() -///////////////////////////////////// -//WAND OF CHAOS - Only spawned by the Staff of Chaos as a rare random effect -///////////////////////////////////// +// WAND OF CHAOS - Only spawned by the Staff of Chaos as a rare random effect + /obj/item/gun/magic/wand/chaos name = "wand of chaos" desc = "Payback time!" diff --git a/code/modules/reagents/chemistry/reagents/drinks_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks_reagents.dm index 86e51200e801..947ac4fe001a 100644 --- a/code/modules/reagents/chemistry/reagents/drinks_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks_reagents.dm @@ -173,32 +173,34 @@ id = "banana" description = "The raw essence of a banana." color = "#F6F834" + process_flags = ORGANIC | SYNTHETIC drink_icon = "banana" drink_name = "Glass of banana juice" drink_desc = "The raw essence of a banana. HONK" taste_description = "banana juice" -/datum/reagent/consumable/drink/banana/on_mob_life(mob/living/M) +/datum/reagent/consumable/drink/banana/on_mob_life(mob/living/carbon/human/M) var/update_flags = STATUS_UPDATE_NONE if(HAS_TRAIT(M, TRAIT_COMIC_SANS) || issmall(M)) - update_flags |= M.adjustBruteLoss(-1, FALSE) - update_flags |= M.adjustFireLoss(-1, FALSE) + update_flags |= M.adjustBruteLoss(-1, FALSE, robotic = TRUE) + update_flags |= M.adjustFireLoss(-1, FALSE, robotic = TRUE) return ..() | update_flags /datum/reagent/consumable/drink/nothing name = "Nothing" id = "nothing" description = "Absolutely nothing." + process_flags = ORGANIC | SYNTHETIC drink_icon = "nothing" drink_name = "Nothing" drink_desc = "Absolutely nothing." taste_description = "nothing... how?" -/datum/reagent/consumable/drink/nothing/on_mob_life(mob/living/M) +/datum/reagent/consumable/drink/nothing/on_mob_life(mob/living/carbon/human/M) var/update_flags = STATUS_UPDATE_NONE if(ishuman(M) && M.mind && M.mind.miming) - update_flags |= M.adjustBruteLoss(-1, FALSE) - update_flags |= M.adjustFireLoss(-1, FALSE) + update_flags |= M.adjustBruteLoss(-1, FALSE, robotic = TRUE) + update_flags |= M.adjustFireLoss(-1, FALSE, robotic = TRUE) return ..() | update_flags /datum/reagent/consumable/drink/potato_juice @@ -401,16 +403,17 @@ id = "bananahonk" description = "A drink from Clown Heaven." color = "#664300" // rgb: 102, 67, 0 + process_flags = ORGANIC | SYNTHETIC drink_icon = "bananahonkglass" drink_name = "Banana Honk" drink_desc = "A drink from Banana Heaven." taste_description = "HONK" -/datum/reagent/consumable/drink/bananahonk/on_mob_life(mob/living/M) +/datum/reagent/consumable/drink/bananahonk/on_mob_life(mob/living/carbon/human/M) var/update_flags = STATUS_UPDATE_NONE if(HAS_TRAIT(M, TRAIT_COMIC_SANS) || issmall(M)) - update_flags |= M.adjustBruteLoss(-1, FALSE) - update_flags |= M.adjustFireLoss(-1, FALSE) + update_flags |= M.adjustBruteLoss(-1, FALSE, robotic = TRUE) + update_flags |= M.adjustFireLoss(-1, FALSE, robotic = TRUE) return ..() | update_flags /datum/reagent/consumable/drink/silencer @@ -418,16 +421,17 @@ id = "silencer" description = "A drink from Mime Heaven." color = "#664300" // rgb: 102, 67, 0 + process_flags = ORGANIC | SYNTHETIC drink_icon = "silencerglass" drink_name = "Silencer" drink_desc = "A drink from mime Heaven." taste_description = "mphhhh" -/datum/reagent/consumable/drink/silencer/on_mob_life(mob/living/M) +/datum/reagent/consumable/drink/silencer/on_mob_life(mob/living/carbon/human/M) var/update_flags = STATUS_UPDATE_NONE if(ishuman(M) && (M.job in list("Mime"))) - update_flags |= M.adjustBruteLoss(-1, FALSE) - update_flags |= M.adjustFireLoss(-1, FALSE) + update_flags |= M.adjustBruteLoss(-1, FALSE, robotic = TRUE) + update_flags |= M.adjustFireLoss(-1, FALSE, robotic = TRUE) return ..() | update_flags /datum/reagent/consumable/drink/chocolatepudding diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm index 6aa411577551..aecf54736c41 100644 --- a/code/modules/reagents/chemistry/reagents/medicine.dm +++ b/code/modules/reagents/chemistry/reagents/medicine.dm @@ -166,18 +166,19 @@ heart_rate_decrease = 1 taste_description = "a safe refuge" goal_difficulty = REAGENT_GOAL_NORMAL + data = list() /datum/reagent/medicine/cryoxadone/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume, show_message = TRUE) if(iscarbon(M)) + data["method"] = method if(method == REAGENT_INGEST && M.bodytemperature < TCRYO) - data = "Ingested" if(show_message) to_chat(M, "[src] freezes solid as it enters your body!") //Burn damage already happens on ingesting ..() /datum/reagent/medicine/cryoxadone/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - if(M.bodytemperature < TCRYO && data != "Ingested") + if(M.bodytemperature < TCRYO && data["method"] == REAGENT_TOUCH) update_flags |= M.adjustCloneLoss(-4, FALSE) update_flags |= M.adjustOxyLoss(-10, FALSE) update_flags |= M.adjustToxLoss(-3, FALSE) @@ -282,6 +283,7 @@ return ..() | update_flags /datum/reagent/medicine/heal_on_apply + data = list() /datum/reagent/medicine/heal_on_apply/proc/heal_external_limb(obj/item/organ/external/organ, volume) return @@ -314,6 +316,7 @@ if(!iscarbon(M)) return ..() + data["method"] = method if(ishuman(M) && volume > 20 && method == REAGENT_TOUCH) heal_overall_damage(M, 20) var/applied_volume = splash_human(M, volume - 20) @@ -336,8 +339,9 @@ /datum/reagent/medicine/heal_on_apply/synthflesh/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustBruteLoss(-1.25 * REAGENTS_EFFECT_MULTIPLIER, FALSE) - update_flags |= M.adjustFireLoss(-1.25 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + if(data["method"] == REAGENT_TOUCH || volume > 1) + update_flags |= M.adjustBruteLoss(-1.25 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + update_flags |= M.adjustFireLoss(-1.25 * REAGENTS_EFFECT_MULTIPLIER, FALSE) return ..() | update_flags /datum/reagent/medicine/heal_on_apply/synthflesh/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume, show_message = 1) @@ -377,7 +381,8 @@ /datum/reagent/medicine/heal_on_apply/styptic_powder/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustBruteLoss(-2 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + if(data["method"] == REAGENT_TOUCH || volume > 1) + update_flags |= M.adjustBruteLoss(-2 * REAGENTS_EFFECT_MULTIPLIER, FALSE) return ..() | update_flags /datum/reagent/medicine/heal_on_apply/styptic_powder/heal_external_limb(obj/item/organ/external/organ, volume) @@ -411,7 +416,8 @@ /datum/reagent/medicine/heal_on_apply/silver_sulfadiazine/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustFireLoss(-2 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + if(data["method"] == REAGENT_TOUCH || volume > 1) + update_flags |= M.adjustFireLoss(-2 * REAGENTS_EFFECT_MULTIPLIER, FALSE) return ..() | update_flags /datum/reagent/medicine/heal_on_apply/silver_sulfadiazine/heal_external_limb(obj/item/organ/external/organ, volume) diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index 5c5a2f96b85d..4ea9e6c141e6 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -438,13 +438,13 @@ description = "A toxin that affects the stamina of a person when injected into the bloodstream." reagent_state = LIQUID color = "#6E2828" - data = 13 taste_description = "bitterness" + var/damage_per_cycle = 13 /datum/reagent/staminatoxin/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustStaminaLoss(REAGENTS_EFFECT_MULTIPLIER * data, FALSE) - data = max(data - 1, 3) + update_flags |= M.adjustStaminaLoss(damage_per_cycle * REAGENTS_EFFECT_MULTIPLIER, FALSE) + damage_per_cycle = max(damage_per_cycle - 1, 3) return ..() | update_flags diff --git a/code/modules/reagents/reagent_containers/iv_bag.dm b/code/modules/reagents/reagent_containers/iv_bag.dm index c6a23a907e49..9058e93a695f 100644 --- a/code/modules/reagents/reagent_containers/iv_bag.dm +++ b/code/modules/reagents/reagent_containers/iv_bag.dm @@ -47,19 +47,17 @@ /obj/item/reagent_containers/iv_bag/proc/begin_processing(mob/target) injection_target = target - ADD_TRAIT(injection_target, TRAIT_HAS_IV_BAG, UID()) RegisterSignal(injection_target, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) START_PROCESSING(SSobj, src) /obj/item/reagent_containers/iv_bag/proc/end_processing() if(injection_target) - REMOVE_TRAIT(injection_target, TRAIT_HAS_IV_BAG, UID()) UnregisterSignal(injection_target, COMSIG_PARENT_EXAMINE) - injection_target = null + injection_target = null STOP_PROCESSING(SSobj, src) /obj/item/reagent_containers/iv_bag/process() - if(!injection_target) + if(QDELETED(injection_target)) end_processing() return @@ -115,9 +113,6 @@ else // Inserting the needle if(!L.can_inject(user, TRUE)) return - if(HAS_TRAIT(target, TRAIT_HAS_IV_BAG)) - to_chat(user, "[target] already [target.p_have()] another IV bag inserted into [target.p_them()]!") - return if(amount_per_transfer_from_this > 10) // We only want to be able to transfer 1, 5, or 10 units to people. Higher numbers are for transfering to other containers to_chat(user, "The IV bag can only be used on someone with a transfer amount of 1, 5 or 10.") return diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 8945b2e73c60..ffe7d3036b14 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -24,10 +24,13 @@ ADD_TRAIT(src, TRAIT_CAN_POINT_WITH, ROUNDSTART_TRAIT) /obj/item/reagent_containers/spray/afterattack(atom/A, mob/user) - if(isstorage(A) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \ + if(isstorage(A) || ismodcontrol(A) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \ || istype(A, /obj/item/reagent_containers) || istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics)) return + if(loc != user) + return + if(istype(A, /obj/structure/reagent_dispensers) && get_dist(src,A) <= 1) //this block copypasted from reagent_containers/glass, for lack of a better solution if(!A.reagents.total_volume && A.reagents) to_chat(user, "[A] is empty.") diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index b5525814fec5..ec0ba2ac4c11 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -294,6 +294,19 @@ /obj/machinery/disposal/deliveryChute/update() return +/obj/machinery/disposal/deliveryChute/CanPass(atom/movable/mover, turf/target, height) + // If the mover is a thrownthing passing through space, remove its thrown datum, + // ingest it like normal, and mark the chute as not passible. + // This prevents the mover from Entering the chute's turf + // while also bypassing thrownthing's /finalize, which would + // cause damage to the chute. + if(mover.throwing && !has_gravity(get_turf(mover))) + qdel(mover.throwing) + Bumped(mover) + return FALSE + + . = ..() + /obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute if(isprojectile(AM) || isAI(AM) || QDELETED(AM)) return diff --git a/code/modules/redis/callbacks/dev_callback.dm b/code/modules/redis/callbacks/dev_callback.dm new file mode 100644 index 000000000000..ea192d6440dd --- /dev/null +++ b/code/modules/redis/callbacks/dev_callback.dm @@ -0,0 +1,11 @@ +// Relays messages to developer +/datum/redis_callback/developer_in + channel = "byond.devsay" + +/datum/redis_callback/developer_in/on_message(message) + var/list/data = json_decode(message) + if(data["source"] == GLOB.configuration.system.instance_id) // Ignore self messages + return + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_DEV_TEAM, FALSE, C.mob)) + to_chat(C, "DEV: [data["author"]]@[data["source"]]: [html_encode(data["message"])]") diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 214f12af72d4..3173b650dbae 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -904,8 +904,8 @@ category = list("Exosuit Equipment") /datum/design/mech_carbine - name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)" - desc = "Allows for the construction of FNX-99 \"Hades\" Carbine." + name = "Exosuit Weapon (FNX-66 \"Hades\" Carbine)" + desc = "Allows for the construction of FNX-66 \"Hades\" Carbine." id = "mech_carbine" build_type = MECHFAB req_tech = list("combat" = 5, "materials" = 4) diff --git a/code/modules/station_goals/bluespace_tap.dm b/code/modules/station_goals/bluespace_tap.dm index 7222ffba2567..aae9f1755e69 100644 --- a/code/modules/station_goals/bluespace_tap.dm +++ b/code/modules/station_goals/bluespace_tap.dm @@ -174,9 +174,6 @@ /obj/item/food/sliceable/xenomeatbread //maybe add some dangerous/special food here, ie robobuger? ) -#define kW *1000 -#define MW kW *1000 -#define GW MW *1000 #define BASE_ENERGY_CONVERSION 4e-6 #define BASE_POINTS 2 @@ -205,8 +202,8 @@ luminosity = 1 /// Correspond to power required for a mining level, first entry for level 1, etc. - var/list/power_needs = list(1 kW, 2 kW, 5 kW, 10 kW, 15 kW, - 25 kW, 50 kW, 100 kW, 250 kW, 500 kW, + var/list/power_needs = list(1 KW, 2 KW, 5 KW, 10 KW, 15 KW, + 25 KW, 50 KW, 100 KW, 250 KW, 500 KW, 1 MW, 2 MW, 5 MW, 10 MW, 15 MW, 20 MW, 25 MW, 30 MW, 40 MW, 50 MW, 60 MW, 70 MW, 80 MW, 90 MW, 100 MW) @@ -583,8 +580,5 @@

Device highly experimental. Not for sale. Do not operate near small children or vital NT assets. Do not tamper with machine. In case of existential dread, stop machine immediately. \ Please document any and all extradimensional incursions. In case of imminent death, please leave said documentation in plain sight for clean-up teams to recover.

" -#undef kW -#undef MW -#undef GW #undef BASE_ENERGY_CONVERSION #undef BASE_POINTS diff --git a/code/modules/supply/supply_packs/pack_security.dm b/code/modules/supply/supply_packs/pack_security.dm index cd580d87dc25..f95c3cbc5756 100644 --- a/code/modules/supply/supply_packs/pack_security.dm +++ b/code/modules/supply/supply_packs/pack_security.dm @@ -334,7 +334,7 @@ /obj/item/clothing/head/soft/sec/corp, /obj/item/clothing/head/soft/sec/corp, /obj/item/clothing/under/rank/security/warden/corporate, - /obj/item/clothing/head/beret/sec/warden, + /obj/item/clothing/head/beret/warden, /obj/item/clothing/under/rank/security/head_of_security/corporate, /obj/item/clothing/head/beret/hos) cost = 200 diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 17f9341bcdec..1edd427daad6 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -253,7 +253,7 @@ /obj/item/organ/internal/cyberimp/arm/janitorial_abductor name = "alien janitorial toolset implant" desc = "A set of alien janitorial tools, designed to be installed on subject's arm." - origin_tech = "materials=5;engineering=5;biotech=5;powerstorage=4;abductor=3" + origin_tech = "materials=5;engineering=5;biotech=5;powerstorage=4;abductor=2" contents = newlist(/obj/item/mop/advanced/abductor, /obj/item/soap/syndie/abductor, /obj/item/lightreplacer/bluespace/abductor, /obj/item/holosign_creator/janitor, /obj/item/melee/flyswatter/abductor, /obj/item/reagent_containers/spray/cleaner/safety/abductor) action_icon = list(/datum/action/item_action/organ_action/toggle = 'icons/obj/abductor.dmi') action_icon_state = list(/datum/action/item_action/organ_action/toggle = "janibelt_abductor") diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index 1a012ae609c2..ca7740d1a309 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -56,6 +56,28 @@ attack_verb = list("burnt") tool_behaviour = TOOL_CAUTERY +/obj/item/cautery/attack(mob/living/target, mob/living/user) + if(!cigarette_lighter_act(user, target)) + return ..() + +/obj/item/cautery/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(target == user) + user.visible_message( + "[user] presses [src] against [cig], heating it until it lights.", + "You press [src] against [cig], heating it until it lights." + ) + else + user.visible_message( + "[user] presses [src] against [cig] for [target], heating it until it lights.", + "You press [src] against [cig] for [target], heating it until it lights." + ) + cig.light(user, target) + return TRUE + /obj/item/cautery/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) @@ -146,6 +168,28 @@ damtype = "fire" hitsound = 'sound/weapons/sear.ogg' +/obj/item/scalpel/laser/attack(mob/living/carbon/target, mob/living/user) + if(!cigarette_lighter_act(user, target)) + return ..() + +/obj/item/scalpel/laser/cigarette_lighter_act(mob/living/user, mob/living/target, obj/item/direct_attackby_item) + var/obj/item/clothing/mask/cigarette/cig = ..() + if(!cig) + return !isnull(cig) + + if(target == user) + user.visible_message( + "[user] presses [src] against [cig], heating it until it lights.", + "You press [src] against [cig], heating it until it lights." + ) + else + user.visible_message( + "[user] presses [src] against [cig] for [target], heating it until it lights.", + "You press [src] against [cig] for [target], heating it until it lights." + ) + cig.light(user, target) + return TRUE + /// lasers also count as catuarys /obj/item/scalpel/laser/laser1 name = "laser scalpel" diff --git a/code/modules/tgui_input/say_modal/tgui_say_speech.dm b/code/modules/tgui_input/say_modal/tgui_say_speech.dm index 8843a80431bd..745f25a7b387 100644 --- a/code/modules/tgui_input/say_modal/tgui_say_speech.dm +++ b/code/modules/tgui_input/say_modal/tgui_say_speech.dm @@ -36,6 +36,9 @@ if(DSAY_CHANNEL) client.dsay(entry) return TRUE + if(DEV_CHANNEL) + client.cmd_dev_say(entry) + return TRUE return FALSE /** diff --git a/code/modules/vehicle/tg_vehicles/tg_vehicles.dm b/code/modules/vehicle/tg_vehicles/tg_vehicles.dm index 7687e66c8c62..5018f41e603b 100644 --- a/code/modules/vehicle/tg_vehicles/tg_vehicles.dm +++ b/code/modules/vehicle/tg_vehicles/tg_vehicles.dm @@ -10,7 +10,6 @@ blocks_emissive = EMISSIVE_BLOCK_GENERIC buckle_lying = FALSE can_buckle = TRUE - buckle_lying = 0 pass_flags = PASSTABLE COOLDOWN_DECLARE(message_cooldown) COOLDOWN_DECLARE(cooldown_vehicle_move) diff --git a/config/example/config.toml b/config/example/config.toml index 8ef429954a90..d9fd1b9b6c04 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -66,6 +66,8 @@ enable_localhost_autoadmin = true # +MENTOR = Access only to the Question's Ahelp and has little way of metagaming the game. # +PROCCALL = Access to call procs on anything. Be careful who you grant this too! # +VIEWRUNTIMES = Allows a player to view the runtimes of the server, but not use other debug verbs +# +MAINTAINER = Access to super dangerous debug verbs that can break an entire round. Use sparingly. +# +DEV_TEAM = Access to dev team chat admin_ranks = [ # Format: {name = "My Rank", rights = ["+LIST", "+OF", "+RIGHTS"]} { name = "Mentor", rights = [ @@ -101,6 +103,9 @@ admin_ranks = [ { name = "Maintainers", rights = [ "+EVERYTHING", ] }, + { name = "Developer", rights = [ + "+DEV_TEAM", + ] }, ] # List of people and the admin rank they are assigned to admin_assignments = [ @@ -116,7 +121,7 @@ admin_rank_colour_map = [ { name = "Community Manager", colour = "#e91e63" }, { name = "Game Admin", colour = "#238afa" }, { name = "Trial Admin", colour = "#7fb6fc" }, - { name = "PR Reviewer", colour = "#c27c0e" }, + { name = "Developer", colour = "#61b413" }, { name = "Mentor", colour = "#f1c40f" }, ] # Map of common CIDs that should not be banned, plus their reasons @@ -211,7 +216,7 @@ mentor_webhook_urls = [ # List of all webhook URLs for the admin feed (round alerts, ahelps, etc) admin_webhook_urls = ["https://admin.webhook.one", "https://admin.webhook.two"] # Role ID for the admin role on the discord. Set to "" to disable. -# THESE MUST BOTH BE STRINGS. BYOND DOESNT LIKE NUMBERS THIS BIG +# THESE MUST BE STRINGS. BYOND DOESNT LIKE NUMBERS THIS BIG admin_role_id = "" # Role ID for the mentor role on the discord. Set to "" to disable. mentor_role_id = "" diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..4084cf286e70 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1,112 @@ +# Contributor Code of Conduct + +## Important + +The Paradise GitHub is not exempt from [Paradise Station community and server rules][rules], +especially rules 0, 1, and 4. An inability to abide by the rules on the +GitHub will result in disciplinary action up to, or including, a repository ban. + +[rules]: https://www.paradisestation.org/rules/ + +## General Expectations + +### PR Discussion + +Comments on Pull Requests should remain relevant to the PR in question and not +derail discussions. + +Under no circumstances are users to be attacked for their ideas or +contributions. While constructive criticism is encouraged, toxicity or general +mean-spirited behaviour will not be tolerated. All participants on a given PR or +issue are expected to be civil. Failure to do so will result in disciplinary +action. + +"Merge-nagging" or similar behaviour is not acceptable. Comments of this nature +will result in warnings or an outright ban from the repository depending on +general behaviour. + +If you exhibit behaviour that's considered to be a net-negative to the community +(offensive commentary, repeat violations, constant nagging, personal attacks, +etc.), you may be banned from other Paradise services (Discord, forums, server, +wiki, etc.) + +Headcoders reserve the right to permanently revoke access from the repository if +your behaviour is considered to be a net negative. + +### PR Approval/Objection Info + +Headcoders (who will take into account the votes from the relevant teams) have +the final say on Pull Requests. While thumbs-up/thumbs-down reaction ratios are +generally taken into account, they do not dictate whether or not a PR will be +merged. + +After a twenty four hour minimum waiting period, Pull Requests can be merged +once they receive approval from the relevant team. An exception is made for +refactors and fixes, which may be merged by any member with commit access' +discretion with no waiting period. + +While normally provided, voting team members are not obligated to publicly state +their objections to a Pull Request. Attacking or berating a voting team member +over an objection will not be tolerated. Additionally, whining over the closure +of a PR, the existence of an objection, or similar behaviour, will not be +tolerated. + +Headcoders may close your PR at their discretion if your PR history has little +focus on improving repo maintainability (ie: making nothing but 20 balance or +feature PRs). Likewise, balance PRs may be closed if the PR author has +little-to-no time played on the server. This is to ensure balance changes are +made by people actually in-touch with the server atmosphere. + +### PR Expectations + +Contributors may only have a maximum of **2** feature or balance Pull Requests +open at any given time. Any additional Pull Requests beyond this limit will be +closed at the discretion of the Headcoders. The Headcoders may grant an +exemption to this limit on a case-by-case basis, as the need arises. + +All Pull Requests are expected to be tested prior to submission. If a submitted +Pull Request fails to pass CI checks, the likelihood of it being merged will be +significantly lower. If you can't take the time to compile/test your Pull +Request, do not expect a warm reception. + +Barring highly specific circumstances (such as single line changes, submissions +from advanced users, or changes to repo documentation), we will not accept Pull +Requests utilising the web editor. + +Pull Requests regarding heavy-handed nerfs, particularly immediately after said +mechanic was used, will be tagged with `I ded pls nerf`. A bad experience with a +particular mechanic is not a justification for nerfing it. + +Reactionary revert PRs are not tolerated under any circumstances. Posting a +revert immediately after a Pull Request is merged will result in a repo-ban. + +It is expected that contributors discuss larger changes on the +[Paradise Station forums](https://www.paradisestation.org/forum/91-code-discussion/), +[GitHub discussions tab](https://github.com/ParadiseSS13/Paradise/discussions), +or the [Discord project-discussion forum](https://discord.com/channels/145533722026967040/1110966752898207824) +prior to starting work on a Pull Request. The amount of time spent on any given +Pull Request is not relevant. Repo staff are not responsible for contributors +wasting their time creating features nobody asked for. Be sure to inform the +corresponding teams about the forum post or discussion. + +For changes to content listed below, contributors **must** obtain approval from +a headcoder or a member of either the balance, design, mapping, or sprite team +(depending on which teams are relevant to the changes) before opening their Pull +Request. This approval must be displayed in the Pull Request description body in +the form of a screenshot. The Headcoders may grant an exemption to this +requirement on a case-by-case basis, as the need arises. + + + + + +> [!IMPORTANT] +> +>

Currently, changes to the following types of content requires pre-approval:

+> +> - **Security content (excluding fixes, code improvement, refactors, sprites, and mapping changes)** +> - **Antagonist content (excluding fixes, code improvement, refactors, sprites, and mapping changes)** +> - **Species content (excluding fixes, code improvement, and refactors)** +> - **Large changes (for example PRs that touch multiple systems, many files, many lines of code)** +> - **Changes that might be controversial** +> - **Changes with wide-ranging balance or design implications** diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 000000000000..b3b15a1ad335 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,226 @@ +# Contributing Guidelines + +This is the contribution guide for Paradise Station. These guidelines apply to +both new issues and new pull requests. If you are making a pull request, please +refer to the [Pull request](#pull-requests) section, and if you are making an +issue report, please refer to the [Issue Report](#issues) section. + +## Commenting + +If you comment on an active pull request or issue report, make sure your comment +is concise and to the point. Comments on issue reports or pull requests should +be relevant and friendly, not attacks on the author or adages about something +minimally relevant. If you believe an issue report is not a "bug", please point +out specifically and concisely your reasoning in a comment on the issue itself. + +Comments on Pull Requests and Issues should remain relevant to the subject in +question and not derail discussions. + +Under no circumstances are users to be attacked for their ideas or +contributions. All participants on a given PR or issue are expected to be civil. +Failure to do so will result in disciplinary action. + +For more details, see the [Code of Conduct](./CODE_OF_CONDUCT.md). + +## Issues + +The Issues section is not a place to request features, or ask for things to be +changed because you think they should be that way. The Issues section is +specifically for reporting bugs in the code. + +Issue reports should be as detailed as possible, and if applicable, should +include instructions on how to reproduce the bug. + +## Pull Requests + +Players are welcome to participate in the development of this fork and submit +their own pull requests. If the work you are submitting is a new feature, or +affects balance, it is strongly recommended you get approval/traction for it +from our forums before starting the actual development. + +It is expected that all code contributors read and understand the +[Guide to Quality PRs](./contributing/quality_prs.md). + +Keep your pull requests atomic. Each pull request should strive to address one +primary goal, and should not include fixes or changes that aren't related to the +main purpose of the pull request. Unrelated changes should be applied in new +pull requests. In case of mapping PRs that add features - consult a member of +the development team on whether it would be appropriate to split up the PR to +add the feature to multiple maps individually. + +Document and explain your pull requests thoroughly. Failure to do so will delay +a PR as we question why changes were made. This is especially important if +you're porting a PR from another codebase (i.e. /tg/station) and divert from the +original. Explaining with single comment on why you've made changes will help us +review the PR faster and understand your decision making process. + +Any pull request must have a changelog. This is to allow us to know when a PR is +deployed on the live server. Inline changelogs are supported through the format +described [here](#using-the-changelog). + +Pull requests should not have any merge commits except in the case of fixing +merge conflicts for an existing pull request. New pull requests should not have +any merge commits. Use `git rebase` or `git reset` to update your branches, not +`git pull`. + +Please explain why you are submitting the pull request, and how you think your +change will be beneficial to the game. Failure to do so will be grounds for +rejecting the PR. + +If your pull request is not finished, make sure it is at least testable in a +live environment. Pull requests that do not at least meet this requirement may +be closed at maintainer discretion. You may request a maintainer reopen the pull +request when you're ready, or make a new one. + +While we have no issue helping contributors (and especially new contributors) +bring reasonably sized contributions up to standards via the pull request review +process, larger contributions are expected to pass a higher bar of completeness +and code quality _before_ you open a pull request. Maintainers may close such +pull requests that are deemed to be substantially flawed. You should take some +time to discuss with maintainers or other contributors on how to improve the +changes. + +By ticking or leaving ticked the option "Allow edits and access to secrets by +maintainers", either when making a PR or at any time thereafter, you give +permission for repository maintainers to push changes to your branch without +explicit permission. Repository maintainers will avoid doing this unless +necessary, and generally should only use it to apply a merge upstream/master, +rebuild TGUI, deconflict maps, or other minor changes required shortly before a +PR is to be merged. More extensive changes such as force-pushes to your branch +require explicit permission from the PR author each time such a change needs to +be made. + +### Using The Changelog + +- The tags able to be used in the changelog are: `add/soundadd/imageadd`, + `del/sounddel/imagedel`, `tweak`, `fix`, `wip`, `spellcheck`, and + `experiment`. +- Without specifying a name it will default to using your GitHub name. Some + examples include: + +```txt + :cl: + add: The ability to change the color of wires + del: Deleted depreciated wire merging now handled in parent + fix: Moving wires now follows the user input instead of moving the stack + /:cl: +``` + +```txt + :cl: UsernameHere + spellcheck: Fixes some misspelled words under Using Changelog + /:cl: +``` + +### PR Status + +Status of your pull request will be communicated via PR labels. This includes: + +- `Status: Awaiting type assignment` - This will be displayed when your PR is + awaiting an internal type assignment (for Fix, Balance, Tweak, etc) +- `Status: Awaiting approval` - This will be displayed if your PR is waiting for + approval from the specific party, be it Balance or Design. Fixes & Refactors + should never have this label +- `Status: Awaiting review` - This will be displayed when your PR has passed the + design vote and is now waiting for someone in the review team to approve it +- `Status: Awaiting merge` - Your PR is done and is waiting for someone with + commit access to merge it. **Note: Your PR may be delayed if it is pending + testmerge or in the mapping queue** + +### Mapping Standards + +All PRs which modify maps are expected to follow all of our +[mapping requirements](./mapping/requirements.md). + +## Modifying MILLA + +Our atmos engine, MILLA, is in the `milla/` directory. It's written in Rust for +performance reasons, which means it's not compiled the same way as the rest of +the code. If you're on Windows, you get a pre-built copy by default. If you're +on Linux, you built one already to run the server. + +If you make changes to MILLA, you'll want to rebuild. This will be very similar +to RUSTG: https://github.com/ParadiseSS13/rust-g The only difference is that you +run `cargo` from the `milla/` directory, and don't need to specify +`--all-features` (though it doesn't hurt). + +The server will automatically detect that you have a local build, and use that +over the default Windows one. + +When you're ready to make a PR, please DO NOT modify `milla.dll` or +`tools/ci/libmilla_ci.so`. Leave "Allow edits and access to secrets by +maintainers" enabled, and post a comment on your PR saying `!build_milla`. A bot +will automatically build them for you and update your branch. + +## Other Notes + +- Bloated code may be necessary to add a certain feature, which means there has + to be a judgement over whether the feature is worth having or not. You can + help make this decision easier by making sure your code is modular. + +- You are expected to help maintain the code that you add, meaning that if there + is a problem then you are likely to be approached in order to fix any issues, + runtimes, or bugs. + +- If you used regex to replace code during development of your code, post the + regex in your PR for the benefit of future developers and downstream users. + +- All new var/proc names should use the American English spelling of words. This + is for consistency with BYOND. + +- All mentions of the company "Nanotrasen" should be written as such - + 'Nanotrasen'. Use of CamelCase (NanoTrasen) is no longer proper. + +- If you are making a PR that adds a config option to change existing behaviour, + said config option must default to as close to as current behaviour as + possible. + +## GitHub Staff + +There are three roles on the GitHub: + +- Headcoder +- Commit Access +- Review Team + +Each role inherits the lower role's responsibilities (IE: Headcoders also have +commit access, and members of commit access are also part of the review team) + +`Headcoders` are the overarching "administrators" of the repository. People +included in this role are: + +- [farie82](https://github.com/farie82) +- [S34N](https://github.com/S34NW) +- [SteelSlayer](https://github.com/SteelSlayer) + +--- + +`Commit Access` members have write access to the repository and can merge your +PRs. People included in this role are: + +- [AffectedArc07](https://github.com/AffectedArc07) +- [Burzah](https://github.com/Burzah) +- [Charliminator](https://github.com/hal9000PR) +- [Contrabang](https://github.com/Contrabang) +- [DGamerL](https://github.com/DGamerL) +- [lewcc](https://github.com/lewcc) + +--- + +`Review Team` members are people who are denoted as having reviews which can +affect mergeability status. People included in this role are: + +- [Burzah](https://github.com/Burzah) +- [Charliminator](https://github.com/hal9000PR) +- [Contrabang](https://github.com/Contrabang) +- [DGamerL](https://github.com/DGamerL) +- [FunnyMan3595](https://github.com/FunnyMan3595) +- [Henri215](https://github.com/Henri215) +- [lewcc](https://github.com/lewcc) +- [Sirryan2002](https://github.com/Sirryan2002) +- [Warriorstar](https://github.com/warriorstar-orion) + +--- + +Full information on the GitHub contribution workflow & policy can be found at +. diff --git a/docs/coding/coding_requirements.md b/docs/coding/coding_requirements.md new file mode 100644 index 000000000000..151f26bc0d38 --- /dev/null +++ b/docs/coding/coding_requirements.md @@ -0,0 +1,867 @@ +# Coding Requirements + +Coders are expected to follow these specifications in order to make everyone's +lives easier. It'll save both your time and ours, by making sure you don't have +to make any changes and we don't have to ask you to. + +## Object Oriented Code + +As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code +must be object-oriented when possible in order to be more flexible when adding +content to it. If you don't know what "object-oriented" means, we highly +recommend you do some light research to grasp the basics. + +## Use absolute pathing + +DM will allow you nest almost any type keyword into a block, as in the following: + +```dm +datum + datum1 + var + varname1 = 1 + varname2 + static + varname3 + varname4 + proc + proc1() + code + proc2() + code + + datum2 + varname1 = 0 + proc + proc3() + code + proc2() + ..() + code +``` + +The use of this format is **not** allowed in this project, as it makes finding +definitions via full text searching next to impossible. The only exception is +the variables of an object may be nested to the object, but must not nest +further. + +The previous code made compliant: + +```dm +/datum/datum1 + var/varname1 = 1 + var/varname2 + var/static/varname3 + var/static/varname4 + +/datum/datum1/proc/proc1() + code + +/datum/datum1/proc/proc2() + code + +/datum/datum1/datum2 + varname1 = 0 + +/datum/datum1/datum2/proc/proc3() + code + +/datum/datum1/datum2/proc2() + ..() + code +``` + +## Do not compare boolean values to `TRUE` or `FALSE` + +Do not compare boolean values to `TRUE` or `FALSE`. For `TRUE` you should just +check if there's a value in that address. For `FALSE` you should use the `!` +operator. An exception is made to this when working with JavaScript or other +external languages. If a function/variable can contain more values beyond `null` +or 0 or `TRUE`, use numbers and defines instead of true/false comparisons. + +```dm +// Bad +var/thing = pick(TRUE, FALSE) +if(thing == TRUE) + return "bleh" +var/other_thing = pick(TRUE, FALSE) +if(other_thing == FALSE) + return "meh" + +// Good +var/thing = pick(TRUE, FALSE) +if(thing) + return "bleh" +var/other_thing = pick(TRUE, FALSE) +if(!other_thing) + return "meh" +``` + +## Use `pick(x, y, z)`, not `pick(list(x, y, z))` + +`pick()` takes a fixed set of options. Wrapping them in a list is +redundant and slightly less efficient. + +```dm +// Bad +var/text = pick(list("test_1", "test_2", "test_3")) +to_chat(world, text) + +// Good +var/text = pick("test_1", "test_2", "test_3") +to_chat(world, text) +``` + +## User Interfaces + +All new user interfaces in the game must be created using the TGUI framework. +Documentation can be found inside the [`tgui/docs`][tgui_docs] folder, and the +[`README.md`][tgui_readme] file. This is to ensure all ingame UIs are +snappy and responsive. An exception is made for user interfaces which are +purely for OOC actions (Such as character creation, or anything admin related) + +[tgui_docs]: https://github.com/ParadiseSS13/Paradise/tree/master/tgui/docs +[tgui_readme]: https://github.com/ParadiseSS13/Paradise/blob/master/tgui/README.md + +## No overriding type safety checks + +The use of the [`:`][colon] "runtime search" operator to override type safety +checks is not allowed. Variables must be casted to the proper type. + +[colon]: http://www.byond.com/docs/ref/#/operator/: + +## Do not chain proc calls and variable access + +The use of the pointer operator, `.`, should not be used to access the return +values of functions directly. This can cause unintended behavior and is +difficult to read. + +```dm +//Bad +var/our_x = get_turf(thing).x + +//Good +var/turf/our_turf = get_turf(thing) +var/our_x = our_turf.x +``` + +## Type paths must begin with a / + +e.g.: `/datum/thing`, not `datum/thing` + +## Datum type paths must began with "datum" + +In DM, this is optional, but omitting it makes finding definitions harder. To be +specific, you can declare the path `/arbitrary`, but it will still be, in +actuality, `/datum/arbitrary`. Write your code to reflect this. + +## Do not use list operators in strings + +The use of list operators to augment strings is not allowed. This is roughly 10 +times slower than using a list with a Join() Function. + +```dm +//Bad +var/text = "text" +text += "More text" +to_chat(world, text) + +//Good +var/list/text = list("text") +text += "More text" +to_chat(world, text.Join("")) +``` + +## Do not use text/string based type paths + +It is rarely allowed to put type paths in a text format, as there are no compile +errors if the type path no longer exists. Here is an example: + +```dm +//Bad +var/path_type = "/obj/item/baseball_bat" + +//Good +var/path_type = /obj/item/baseball_bat +``` + +## Do not use `\The` + +The `\The` macro doesn't actually do anything when used in the format `\The +[atom reference]`. Directly referencing an atom in an embedded string will +automatically prefix `The` or `the` to it as appropriate. As an extension, when +referencing an atom, don't use `[atom.name]`, use `[atom]`. The only exception +to this rule is when dealing with items "belonging" to a mob, in which case you +should use `[mob]'s [atom.name]` to avoid `The` ever forming. + +```dm +//Bad +var/atom/A +"\The [A]" + +//Good +var/atom/A +"[A]" +``` + +## Use the pronoun library instead of `\his` macros + +We have a system in [`code/__HELPERS/pronouns.dm`][pronouns] +for addressing all forms of pronouns. This is useful in a number of ways; + +- BYOND's `\his` macro can be unpredictable on what object it references. Take + this example: `"[user] waves \his [user.weapon] around, hitting \his + opponents!"`. This will end up referencing the user's gender in the first + occurrence, but what about the second? It'll actually print the gender set on + the weapon he's carrying, which is unintended - and there's no way around + this. +- It always prints the real `gender` variable of the atom it's referencing. This + can lead to exposing a mob's gender even when their face is covered, which + would normally prevent it's gender from being printed. + +The way to avoid these problems is to use the pronoun system. Instead of +`"[user] waves \his arms."`, you can do `"[user] waves [user.p_their()] arms."` + +```dm +//Bad +"[H] waves \his hands!" +"[user] waves \his [user.weapon] around, hitting \his opponents!" + +//Good +"[H] waves [H.p_their()] hands!" +"[user] waves [H.p_their()] [user.weapon] around, hitting [H.p_their()] opponents!"` +``` + +[pronouns]: https://github.com/ParadiseSS13/Paradise/blob/master/code/__HELPERS/pronouns.dm + +## Use `[A.UID()]` over `\ref[A]` + +BYOND has a system to pass "soft references" to datums, using the format +`"\ref[datum]"` inside a string. This allows you to find the object just based +off of a text string, which is especially useful when dealing with the bridge +between BYOND code and HTML/JS in UIs. It's resolved back into an object +reference by using `locate("\ref[datum]")` when the code comes back to BYOND. +The issue with this is that `locate()` can return a unexpected datum if the +original datum has been deleted - BYOND recycles the references. + +UID's are actually unique; they work off of a global counter and are not +recycled. Each datum has one assigned to it when it's created, which can be +accessed by [`[datum.UID()]`][duid]. You can use this as a snap-in replacement for +`\ref` by changing any `locate(ref)` calls in your code to `locateUID(ref)`. +Usage of this system is mandatory for any `Topic()` calls, and will produce +errors in Dream Daemon if it's not used. + +```dm +//Bad +"Link!" + +//Good +"Link!" +``` + +[duid]: https://codedocs.paradisestation.org/datum.html#proc/UID + +## Use `var/name` format when declaring variables + +While DM allows other ways of declaring variables, this one should be used for +consistency. + +## Tabs, not spaces + +You must use tabs to indent your code, **not spaces**. You may use spaces to align +text, but you should tab to the block level first, then add the remaining +spaces. + +## No hacky code + +Hacky code, such as adding specific checks (ex: `istype(src, /obj/whatever)`), +is highly discouraged and only allowed when there is **_no_** other option. +(Pro-tip: 'I couldn't immediately think of a proper way so thus there must be no +other option' is not gonna cut it here! If you can't think of anything else, say +that outright and admit that you need help with it. Maintainers, PR Reviewers, +and other contributors who can help you exist for exactly that reason.) + +You can avoid hacky code by using object-oriented methodologies, such as +overriding a function (called "procs" in DM) or sectioning code into functions +and then overriding them as required. + +The same also applies to bugfixes - If an invalid value is being passed into a +proc from something that shouldn't have that value, don't fix it on the proc +itself, fix it at its origin! (Where feasible) + +## No duplicated code + +Copying code from one place to another may be suitable for small, short-time +projects, but Paradise is a long-term project and highly discourages this. + +Instead you can use object orientation, or simply placing repeated code in a +function, to obey this specification easily. + +## Startup/Runtime tradeoffs with lists and the "hidden" init proc + +First, read the comments in [this BYOND thread](http://www.byond.com/forum/?post=2086980&page=2#comment19776775), starting where the link takes you. + +There are two key points here: + +1. Defining a list in the variable's definition calls a hidden proc - init. If + you have to define a list at startup, do so in `New()` (or preferably + `Initialize()`) and avoid the overhead of a second call (`init()` and then + `New()`) + +2. It also consumes more memory to the point where the list is actually + required, even if the object in question may never use it! + +Remember: although this tradeoff makes sense in many cases, it doesn't cover +them all. Think carefully about your addition before deciding if you need to use +it. + +## Prefer `Initialize()` over `New()` for atoms + +Our game controller is pretty good at handling long operations and lag, but it +can't control what happens when the map is loaded, which calls `New()` for all +atoms on the map. If you're creating a new atom, use the `Initialize()` proc to +do what you would normally do in `New()`. This cuts down on the number of proc +calls needed when the world is loaded. + +While we normally encourage (and in some cases, even require) bringing out of +date code up to date when you make unrelated changes near the out of date code, +that is not the case for `New()` -> `Initialize()` conversions. These systems +are generally more dependent on parent and children procs, so unrelated random +conversions of existing things can cause bugs that take months to figure out. + +## No implicit `var/` + +When you declare a parameter in a proc, the `var/` is implicit. Do not include +any implicit `var/` when declaring a variable. + +```dm +//Bad +/obj/item/proc1(var/mob/input1, var/input2) + code + +//Good +/obj/item/proc1(mob/input1, input2) + code +``` + +## No magic numbers or strings + +This means stuff like having a "mode" variable for an object set to "1" or "2" +with no clear indicator of what that means. Make these #defines with a name that +more clearly states what it's for. For instance: + +```dm +//Bad +/datum/proc/do_the_thing(thing_to_do) + switch(thing_to_do) + if(1) + do_stuff() + if(2) + do_other_stuff() +``` + +There's no indication of what "1" and "2" mean! Instead, you should do something +like this: + +```dm +//Good +#define DO_THE_THING_REALLY_HARD 1 +#define DO_THE_THING_EFFICIENTLY 2 + +/datum/proc/do_the_thing(thing_to_do) + switch(thing_to_do) + if(DO_THE_THING_REALLY_HARD) + do_stuff() + if(DO_THE_THING_EFFICIENTLY) + do_other_stuff() +``` + +This is clearer and enhances readability of your code! Get used to doing it! + +## Control statements + +- All control statements comparing a variable to a number should use the formula + of `thing` `operator` `number`, not the reverse (e.g. `if(count <= 10)` not + `if(10 >= count)`) +- All control statements must be spaced as `if()`, with the brackets touching + the keyword. +- All control statements must not contain code on the same line as the + statement. + +```dm +//Bad +if(x) return + +//Good +if(x) + return +``` + +## Player Output + +Due to the use of "TGchat", Paradise requires a special syntax for outputting +text messages to players. Instead of `mob << "message"`, you must use +`to_chat(mob, "message")`. Failure to do so will lead to your code not working. + +## Use guard clauses + +_Guard clauses_ are early returns in a proc for specific conditions. This +is preferred wrapping most of a proc's behavior in an in-block, as procs +will often check a handful of early conditions to bail out on. + +This is bad: + +```dm +/datum/datum1/proc/proc1() + if(thing1) + if(!thing2) + if(thing3 == 30) + do stuff +``` + +This is good: + +```dm +/datum/datum1/proc/proc1() + if(!thing1) + return + if(thing2) + return + if(thing3 != 30) + return + do stuff +``` + +This prevents nesting levels from getting deeper then they need to be. + +## Use `addtimer()` instead of `sleep()` or `spawn()` + +If you need to call a proc after a set amount of time, use `addtimer()` instead +of `spawn()` / `sleep()` where feasible. Though more complex, this method has +greater performance. Additionally, unlike `spawn()` or `sleep()`, it can be +cancelled. For more details, see +[https://github.com/tgstation/tgstation/pull/22933](https://github.com/tgstation/tgstation/pull/22933). + +Look for code examples on how to properly use it. + +```dm +//Bad +/datum/datum1/proc/proc1(target) + spawn(5 SECONDS) + target.dothing(arg1, arg2, arg3) + +//Good +/datum/datum1/proc/proc1(target) + addtimer(CALLBACK(target, PROC_REF(dothing), arg1, arg2, arg3), 5 SECONDS) +``` + +## Signals + +Signals are a slightly more advanced topic, but are often useful for attaching +external behavior to objects that should be triggered when a specific event +occurs. + +When defining procs that should be called by signals, you must include +`SIGNAL_HANDLER` after the proc header. This ensures that no sleeping code can +be called from within a signal handler, as that can cause problems with the +signal system. + +Since callbacks can be connected to many signals with `RegisterSignal`, it can +be difficult to pin down the source that a callback is invoked from. Any new +`SIGNAL_HANDLER` should be followed by a comment listing the signals that the +proc is expected to be invoked for. If there are multiple signals to be handled, +separate them with a `+`. + +```dm +/atom/movable/proc/when_moved(atom/movable/A) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + do_something() + +/datum/component/foo/proc/on_enter(datum/source, atom/enterer) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + COMSIG_ATOM_INITIALIZED_ON + do_something_else() +``` + +If your proc does have something that needs to sleep (such as a `do_after()`), +do not simply omit the `SIGNAL_HANDLER`. Instead, call the sleeping code with +`INVOKE_ASYNC` from within the signal handling function. + +```dm +/atom/movable/proc/when_moved(atom/movable/A) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + INVOKE_ASYNC(src, PROC_REF(thing_that_sleeps), arg1) +``` + +## Operators + +### Spacing of operators + +- Operators that should be separated by spaces: + - Boolean and logic operators like `&&`, `||` `<`, `>`, `==`, etc. (But not `!`) + - Bitwise AND `&` and OR `|`. + - Argument separator operators like `,`. (and `;` when used in a forloop) + - Assignment operators like `=` or `+=` or the like. + - Math operators like `+`, `-`, `/`, or `*`. +- Operators that should NOT be separated by spaces: + - Access operators like `.` and `:`. + - Parentheses `()`. + - Logical not `!`. + +### Use of operators + +- Bitwise ANDs (`&`) should be written as `bitfield & bitflag` NEVER `bitflag & + bitfield`. Both are valid, but the latter is confusing and nonstandard. +- Associated lists declarations must have their key value quoted if it's a string. + +```dm +//Bad +list(a = "b") + +//Good +list("a" = "b") +``` + +### Bitflags + +Bitshift operators are mandatory, opposed to directly typing out the value: + +```dm +#define MACRO_ONE (1<<0) +#define MACRO_TWO (1<<1) +#define MACRO_THREE (1<<2) +``` + +Is accepted, whereas the following is not: + +```dm +#define MACRO_ONE 1 +#define MACRO_TWO 2 +#define MACRO_THREE 4 +``` + +While it may initially look intimidating, `(1<Arbitrary text
") + +//Good +user.visible_message("Arbitrary text") +``` + +- You should not use color macros (`\red, \blue, \green, \black`) to color text, + instead, you should use span classes. `Red text`, + `Blue text`. + +```dm +//Bad +to_chat(user, "\red Red text \black Black text") + +//Good +to_chat(user, "Red textBlack text") +``` + +- To use variables in strings, you should **never** use the `text()` operator, + use embedded expressions directly in the string. + +```dm +//Bad +to_chat(user, text("[] is leaking []!", name, liquid_type)) + +//Good +to_chat(user, "[name] is leaking [liquid_type]!") +``` + +- To reference a variable/proc on the src object, you should **not** use + `src.var`/`src.proc()`. The `src.` in these cases is implied, so you should + just use `var`/`proc()`. + +```dm +//Bad +var/user = src.interactor +src.fill_reserves(user) + +//Good +var/user = interactor +fill_reserves(user) +``` + +## Develop Secure Code + +- Player input must always be escaped safely. We recommend you use + `stripped_input()` in all cases where you would use input. Essentially, just + always treat input from players as inherently malicious and design with that + use case in mind. + +- Calls to the database must be escaped properly; use proper parameters (values + starting with a `:`). You can then replace these with a list of parameters, and + these will be properly escaped during the query, and prevent any SQL + injection. + +```dm +//Bad +var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey='[target_ckey]'") + +//Good +var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey=:target_ckey", list( + "target_ckey" = target_ckey +)) // Note the use of parameters on the above line and :target_ckey in the query. +``` + +- All calls to topics must be checked for correctness. Topic href calls can be + easily faked by clients, so you should ensure that the call is valid for the + state the item is in. Do not rely on the UI code to provide only valid topic + calls, because it won't. + +- Information that players could use to metagame (that is, to identify round + information and/or antagonist type via information that would not be available + to them in character) should be kept as administrator-only. + +- Where you have code that can cause large-scale modification and _FUN_, make + sure you start it out locked behind one of the default admin roles - use + common sense to determine which role fits the level of damage a function could + do. + +## Files + +- Because runtime errors do not give the full path, try to avoid having files + with the same name across folders. + +- File names should not be mixed case, or contain spaces or any character that + would require escaping in a URI. + +- Files and path accessed and referenced by code above simply being `#include`d + should be strictly lowercase to avoid issues on filesystems where case + matters. + +### Modular Code in a File + +- Code should be modular where possible; if you are working on a new addition, + then strongly consider putting it in its own file unless it makes sense to put + it with similar ones (e.g. a new tool would go in the `tools.dm` file). + +- Our codebase also has support for checking files so that they only contain one + specific typepath, including none of its subtypes. This can be done by adding + a specific header at the beginning of the file, which the CI will look for + when running. An example can be seen below. You can also run this test locally + using `/tools/ci/restrict_file_types.py` + +```dm +RESTRICT_TYPE(/datum/foo) + +/datum/proc/do_thing() // Error: '/datum' proc found in a file restricted to '/datum/foo' + +/datum/foo + +/datum/foo/do_thing() + +/datum/foo/bar // Error: '/datum/foo/bar' type definition found in a file restricted to '/datum/foo' + +/datum/foo/bar/do_thing() // Error: '/datum/foo/bar' proc found in a file restricted to '/datum/foo' +``` + +## SQL + +- Do not use the shorthand SQL insert format (where no column names are + specified) because it unnecessarily breaks all queries on minor column changes + and prevents using these tables for tracking outside related info such as in a + connected site/forum. + +- Use parameters for queries, as mentioned above in [Develop Secure Code](#develop-secure-code). + +- Always check your queries for success with `if(!query.warn_execute())`. By + using this standard format, you can ensure the correct log messages are used. + +- Always `qdel()` your queries after you are done with them. This cleans up the + results and helps things run smoother. + +- All changes to the database's layout (schema) must be specified in the + database changelog in SQL, as well as reflected in the schema file. + +- Any time the schema is changed, the `SQL_VERSION` defines must be incremented, + as well as the example config, with an appropriate conversion kit placed in + the `SQL/updates` folder. + +- Queries must never specify the database, be it in code, or in text files in + the repo. + +## Dream Maker Quirks/Tricks + +Like all languages, Dream Maker has its quirks and some of them are beneficial +to us. + +### In-To for-loops + +`for(var/i = 1, i <= some_value, i++)` is a fairly standard way to write an +incremental for loop in most languages (especially those in the C family), but +DM's `for(var/i in 1 to some_value)` syntax is oddly faster than its +implementation of the former syntax; where possible, it's advised to use DM's +syntax. (Note, the `to` keyword is inclusive, so it automatically defaults to +replacing `<=`; if you want `<` then you should write it as `1 to +some_value-1`). + +**However**, if either `some_value` or `i` changes within the body of the for +(underneath the `for(...)` header) or if you are looping over a list **and** +changing the length of the list, then you can **not** use this type of for-loop! + +### `for(var/A in list)` VS `for(var/i in 1 to length(list))` + +The former is faster than the latter, as shown by the following profile results: + +![](./images/for_loop_timing.png) + +Code used for the test: + +```dm +var/list/numbers_to_use = list() +proc/initialize_shit() + for(var/i in 1 to 1000000) + numbers_to_use += rand(1,100000) + +proc/old_loop_method() + for(var/i in numbers_to_use) + var/numvar = i + +proc/new_loop_method() + for(var/i in 1 to numbers_to_use.len) + var/numvar = numbers_to_use[i] +``` + +### `istype()`-less `for` loops + +A name for a differing syntax for writing for-each style loops in DM. It's **not** +DM's standard syntax, hence why this is considered a quirk. Take a look at this: + +```dm +var/list/bag_of_items = list(sword1, apple, coinpouch, sword2, sword3) +var/obj/item/sword/best_sword +for(var/obj/item/sword/S in bag_of_items) + if(!best_sword || S.damage > best_sword.damage) + best_sword = S +``` + +The above is a simple proc for checking all swords in a container and returning +the one with the highest damage, and it uses DM's standard syntax for a for-loop +by specifying a type in the variable of the for's header that DM interprets as a +type to filter by. It performs this filter using `istype()` (or some +internal-magic similar to `istype()` - this is BYOND, after all). This is fine +in its current state for `bag_of_items`, but if `bag_of_items` contained ONLY +swords, or only SUBTYPES of swords, then the above is inefficient. For example: + +```dm +var/list/bag_of_swords = list(sword1, sword2, sword3, sword4) +var/obj/item/sword/best_sword +for(var/obj/item/sword/S in bag_of_swords) + if(!best_sword || S.damage > best_sword.damage) + best_sword = S +``` + +The above code specifies a type for DM to filter by. + +With the previous example that's perfectly fine, we only want swords, but if the +bag only contains swords? Is DM still going to try to filter because we gave it +a type to filter by? YES, and here comes the inefficiency. Wherever a list (or +other container, such as an atom (in which case you're technically accessing +their special contents list, but that's irrelevant)) contains datums of the same +datatype or subtypes of the datatype you require for your loop's body, you can +circumvent DM's filtering and automatic `istype()` checks by writing the loop as +such: + +```dm +var/list/bag_of_swords = list(sword, sword, sword, sword) +var/obj/item/sword/best_sword +for(var/s in bag_of_swords) + var/obj/item/sword/S = s + if(!best_sword || S.damage > best_sword.damage) + best_sword = S +``` + +Of course, if the list contains data of a mixed type, then the above +optimisation is **dangerous**, as it will blindly typecast all data in the list +as the specified type, even if it isn't really that type, causing runtime errors +(aka your shit won't work if this happens). + +### Dot variable + +Like other languages in the C family, DM has a `.` or "Dot" operator, used for +accessing variables/members/functions of an object instance. eg: + +```dm +var/mob/living/carbon/human/H = YOU_THE_READER +H.gib() +``` + +However, DM also has a dot _variable_, accessed just as `.` on its own, +defaulting to a value of null. Now, what's special about the dot operator is +that it is automatically returned (as in the `return` statement) at the end of a +proc, provided the proc does not already manually return (`return count` for +example.) Why is this special? + +With `.` being everpresent in every proc, can we use it as a temporary variable? +Of course we can! However, the `.` operator cannot replace a typecasted variable +- it can hold data any other var in DM can, it just can't be accessed as one, +although the `.` operator is compatible with a few operators that look weird but +work perfectly fine, such as: `.++` for incrementing `.'s` value, or `.[1]` for +accessing the first element of `.`, provided that it's a list. + +### Globals versus static + +DM has a var keyword, called `global`. This var keyword is for vars inside of +types. For instance: + +```dm +/mob + var/global/thing = TRUE +``` + +This does **not** mean that you can access it everywhere like a global var. Instead, it means that that var will only exist once for all instances of its type, in this case that var will only exist once for all mobs - it's shared across everything in its type. (Much more like the keyword `static` in other languages like PHP/C++/C#/Java) + +Isn't that confusing? + +There is also an undocumented keyword called `static` that has the same +behaviour as global but more correctly describes BYOND's behaviour. Therefore, +we always use static instead of global where we need it, as it reduces suprise +when reading BYOND code. + +### Global Vars + +All new global vars must use the defines in +[`code/__DEFINES/_globals.dm`][globals]. Basic usage is as follows: + +To declare a global var: + +```dm +GLOBAL_VAR(my_global_here) +``` + +To access it: + +```dm +GLOB.my_global_here = X +``` + +There are a few other defines that do other things. `GLOBAL_REAL` shouldn't be +used unless you know exactly what you're doing. `GLOBAL_VAR_INIT` allows you to +set an initial value on the var, like `GLOBAL_VAR_INIT(number_one, 1)`. +`GLOBAL_LIST_INIT` allows you to define a list global var with an initial value, +etc. + +[globals]: https://github.com/ParadiseSS13/Paradise/blob/master/code/__DEFINES/_globals.dm diff --git a/docs/coding/debugging.md b/docs/coding/debugging.md new file mode 100644 index 000000000000..6414d2df9727 --- /dev/null +++ b/docs/coding/debugging.md @@ -0,0 +1,304 @@ +# Guide to Debugging + +## Intro +Got a bug and you're unable to find it by just looking at your code? Try +debugging! This guide will teach you the basics of debugging, how to read the +values and some tips and tricks. It will be written as a chronological story. +Where the chapters explain the next part of the debugging process. + +Be sure to look at [Getting Started](../contributing/getting_started.md) if +you're new and need help with setting up your repo. Do also remember that all +below here is how I do it. There are many ways but I find that this works for +me. + +### What Is Debugging +> "Debugging is the process of detecting and removing of existing and potential +errors (also called as "bugs") in a software code that can cause it to behave +unexpectedly or crash." +[(source)](https://economictimes.indiatimes.com/definition/debugging) + +As you can see from this quote. It is a very broad term. This guide will use a +code debugger to step through your code and look at what is happening. + +## How To Debug +We will be using [#15958](https://github.com/ParadiseSS13/Paradise/issues/15958) as an example issue. + +### Finding The Issue +First of all, you need to understand what is happening functionally. This +usually gives you hints as to where it goes wrong. + +Looking at the GitHub issue we can see that the author (luckily) wrote a clear +reproduction. Without one we'd only be guessing as to where it goes wrong +exactly. Here a tripwire mine activates when it is in a container such as a +closed locker or a bag. + +This gives us the hint that the trigger mechanism does not check if the object +is directly on a turf. Using this hint we go look for the proc which causes the +trigger to happen. Using [my previous guide's advice][contrib] +we quickly find `/obj/item/assembly/infra`. + +![image](./images/debug_infra_search_results.png) + +In the current file of `/obj/item/assembly/infra`, "infrared.dm", we can see a +lot of different procs. We're only really interested in the proc which triggers +the bomb. + +![image](./images/debug_trigger_proc.png) + +We see that the proc `toggle_secure` starts and stops processing of the object. +This gives us a hint as to where the triggering happens. Looking at +`/obj/item/assembly/infra/process` we see that it creates beams when it is +active. Those beams are functionally used to trigger the bomb itself. + +![image](./images/debug_infra_process.png) + +Looking at the `/obj/effect/beam/i_beam` object we see that this is indeed the +case. + +![image](./images/debug_ibeam_hit.png) + +`/obj/effect/beam/i_beam/proc/hit()` is defined here which calls `trigger_beam` +on master. `hit()` in term is called whenever something `Bumped` or `Crossed` +the beam. I found this by right clicking on the `hit` proc and choosing `Find +All References` + +![image](./images/debug_ibeam_findreferences.png) + +![image](./images/debug_ibeam_findresults.png) + +So now we know what is causing the triggering of the bomb. We know that beams +are sent when the bomb is active. And we functionally know that these beams are +also sent when the bomb is hidden in a locker or bag. + +[contrib]: ./quickstart.md + +### Breakpoints +Now we know what is happening we can start debugging. I have a suspicion already +of what is the cause of the issue. Namely that the `infrared emitter` does not +check the `loc` of the actual bomb in the `/obj/item/assembly/infra/process()` +proc. + +To confirm this I will place a breakpoint just when the `process` proc begins. +You do this by clicking just left of the line number where you want to put the +breakpoint. + +![image](./images/debug_set_breakpoint.png) + +The red dot there is a breakpoint that is set. Clicking it again removes it. + +After doing this we will follow the reproduction steps. Once the game hits your +breakpoint it will freeze your game and your VS Code instance should pop up to +the front. If not just open VS Code. + +When testing this I noticed that the breakpoint got hit multiple times before I +could do my reproduction. If that is the case then you have multiple options for +combating this annoyance. Either you disable the breakpoint and re-enable it +when you are ready to test. This is not a great way of doing it for this case +but in some cases, this is enough. Or you move the breakpoint to a better +location. I choose this one and I've moved it a bit lower. + +![image](./images/debug_lower_breakpoint.png) + +I moved it past the `if(!on)` check which eliminates all trip wires which are +not turned on. (Quick note. This is really bad code. They should not be +processing when they are not turned on) + +I spawned a premade grenade instead of making my own grenade here. Saves me +quite some time and annoyance in trying to look up how to do this again. +Remember the search results when looking for the trip mine? Yeah, use one of +those. + +### In Debug Mode +Once I turn on the bomb I notice that VS Code indeed pops up to the front. The +game is now paused till you say it can continue. + +![image](./images/debug_game_paused.png) + +The breakpoint line is now highlighted. The highlight shows where the code is +currently. It is about to do the `if(!secured)` check. Let's make the code +execute that one step by stepping over the line. F10 as a shortcut or you can +press the "Step Over" button in your active debugger window. + +![image](./images/debug_stepthrough.png) + +Now the code is executed and the highlight moved to the next line. Step over is +handy to quickly move over your code. It will jump over any proc call. Step Into +(F11) is for when you want to actually step into the proc call. This will be +explained later when it is needed. + +If you step over a bunch of times you will see that it will go and create the +`i_beam`. + +![image](./images/debug_stepthrough_create.png) + +Even though I am currently holding the bomb in my hands (same situation as when +it is in a bag/locker code-wise). Why is this the case? In the code, you can see +that a beam is created when the bomb is `on`, `secured` and `first` and `last` +are both null. These all have nothing to do with our issue. But the last check +checks if `T` is not null. `T` is defined earlier in the proc as +`get_turf(src)`. + +![image](./images/debug_stepthrough_getturf.png) + +In other words. `T` is the turf below my characters feet. And of course, this +one does exist in this case. What is missing here is a check to see if the +actual bomb is on a `turf`. How do we even check this? + +Time to go look for a reference to the actual bomb as `/obj/item/assembly/infra` +is just the mechanism used by the bomb. `/obj/item/assembly/infra` itself is a +subtype of `/obj/item/assembly` which is the base type that is used for bomb +mechanisms. So we best look at the definition of `/obj/item/assembly`. We do +this by `Ctrl`-clicking on the `assembly` part of `/obj/item/assembly/infra`. + +![image](./images/debug_assembly_def.png) + +Here we see the definition. We can see that `/obj/item/assembly` has a variable +called `holder` which is of type `/obj/item/assembly_holder`. This is most +likely the thing we want. + +Now to check if this is correct. Go to your debug window and open `Arguments` +and then `src`. `src` is of course the object we currently are. Which is the +`/obj/item/assembly/infra`. Once it is open you can see a **LOT** of variables. + +![image](./images/debug_arg_vars.png) + +We are not interested in most of them and instead, we want to find `holder` + +![image](./images/debug_holder_var.png) + +Yep, this is the one we want. Now how do we check if this object is directly on +a turf? How do we even check its location? The answer is `loc`. `loc` contains +the location of the object. Here I found out that the `loc` of the +`assembly_holder` wasn't actually my character but instead it was the grenade. +Good thing we checked further before we started coding right? + +![image](./images/debug_mine_location.png) + +### Finding/Making The Right Variable To Use +Now it becomes a bit odd. Chemistry bomb code is fairly ... bad. But we can make +this work. First, we go look a bit more into the code to find the proper +variable to use. We *can* use `holder.loc.loc` but that says very little about +what it actually means. It would cause even more headaches for people working +with the code in the future. Instead, we will help our future co-developers a +bit and look into improving the existing code. Later on, we also see that this +is not the correct way of fixing it fully. + +Let's take a look at the `assembly` code in `assembly.dm`. Let's see how the +assembly determines what its grenade is. When looking around the file I found +the proc `/obj/item/assembly/proc/pulse(radio = FALSE)`. This one seems +promising. + +![image](./images/debug_radio_pulse.png) + +Here we can see that either the holder is used or if `loc` is a grenade it will +be primed using `prime`. As you can see a previous coder even stated that this +is a hack. This however does give me an idea of how to handle this and the edge +cases that exist. Namely the case where a grenade owns the trip laser as a +mechanism. + +The idea I had in mind is to create a proc which returns the physical outer +object. The payload, grenade or such. Where does this proc go? Well in the +assembly file since it will be usable for other code as well. I just put it at +the bottom of the file since nowhere else seemed to fit better. I quickly found +that I needed some more info for this. What if the `holder` was not attached +yet? Or it is remotely attached to a bomb? For this I made a proc for the +`/obj/item/assembly_holder` object which will return the actual outermost +object. + +![image](./images/debug_get_outer_object.png) + +I found out that `master` is the bomb linked by the line at the top of the +image. This all allows me to complete my other proc. + +![image](./images/debug_complete_other_proc.png) + +Now we have a proper method to use to find the outermost object. + +### Applying The Fix And Testing It +Now we can go and fix the issue at hand. We want to check if the outermost +object its `loc` is a turf. If not it should not fire new lasers and kill the +old ones. + +We already have the turf that the `/obj/item/assembly/infra` is located on. Now +we just have to check if that turf is the same turf as our outermost object. + +![image](./images/debug_check_turf.png) + +Now, this should work. But you of course have to test it! Build it and run the +game. Run it without a breakpoint set first to see if it works functionally. + +And it seems to work! Now let's trip it and see if it actually keeps working. + +### Runtimes And Stacktrace Traveling +And the game froze. VS Code began blinking. What is happening? The game ran into +a runtime exception + +![image](./images/debug_runtime.png) + +Now let's see if this is actually our fault or not. Since we did not touch any +timers. To check this go to the debug window and open the call stack. + +![image](./images/debug_call_stack.png) + +Here you can see **ALL** the current "threads" of the game. DM itself is not a +multithreaded language but it can have multiple threads. I won't go into detail +on that here for simplicity sake. The top item is the one you are currently on. +Just click on it to open it. + +![image](./images/debug_top_call_stack.png) + +Here we can see the entire stack trace of our current thread. The stack trace is +the entire path the code took thus far. At the top is the last called proc and +at the bottom is the origin of the call. Clicking on each stack will jump you to +the location in the code. The message said that `addtimer` was called on a +`QDELETED` item. This means that the item it is made for is already deleted. +Lets click on `/obj/item/assembly/infra/trigger_beam()` in the stack trace to go +to that call location. + +![image](./images/debug_addtimer.png) + +Here we see where it went wrong. It seems that `addtimer(CALLBACK(src, +.proc/process_cooldown), 10)` is called even though the `src` is already +deleted. How can this happen? To save us all some time. `pulse` is the proc +which triggers the bomb. In our case our bomb exploded, destroying itself. +Now... is this our fault? No. But we can fix it nonetheless. + +![image](./images/debug_pulse_false.png) + +This code change will ensure the message is sent and that the runtime stops from +happening. + +### Stepping Into VS Stepping Over +Now back to the testing. Once build again start up the game again and try it +this time using a raw `/obj/item/assembly/infra`. As you can see it works only +when you hold it. But not when it is on the floor itself. Seems we made a +mistake! Place a breakpoint again in the `/obj/item/assembly/infra/process()` +proc since there something goes wrong. + +![image](./images/debug_outer_object_breakpoint.png) + +Now we want to step into the current line. This means that we will go into +`get_outer_object`. Press either F11 or the "Step Into" button. + +![image](./images/debug_step_into_button.png) + +This will get us to the proc itself. Now step over till you see it return `loc`. +`loc` here is the turf. Which is not the outermost object. Seems we need to do +another check there. This almost certainly also goes for +`/obj/item/assembly_holder/proc/get_outer_object()` as we used mostly the same +logic there. + +![image](./images/debug_common_logic.png) + +## Outro +Now let's test again. And it seems that there are more issues! The +assembly_holder still shoots a laser if you hold it or put it in a bag. Same for +the infra itself. Now I know the fix already but where is the fun in that. + +I'm leaving the last solution (and honour of making the PR that solves the +issue) open for you! If you feel like testing yourself then please pick up this +issue and fix it the way you think would work. I'd love to see your method! + +Please also let me know what you think of this format of doing a guide. This one +was more a look into how I do it step by step compared to a more structured +guide. diff --git a/docs/coding/images/Game_Panel.png b/docs/coding/images/Game_Panel.png new file mode 100644 index 000000000000..1bdf2569941c Binary files /dev/null and b/docs/coding/images/Game_Panel.png differ diff --git a/docs/coding/images/Game_Panel_Create.png b/docs/coding/images/Game_Panel_Create.png new file mode 100644 index 000000000000..90dc29764cf3 Binary files /dev/null and b/docs/coding/images/Game_Panel_Create.png differ diff --git a/docs/coding/images/Runtime_Viewer.png b/docs/coding/images/Runtime_Viewer.png new file mode 100644 index 000000000000..d5df04bde7c3 Binary files /dev/null and b/docs/coding/images/Runtime_Viewer.png differ diff --git a/docs/coding/images/Runtime_Viewer_View_Runtime.png b/docs/coding/images/Runtime_Viewer_View_Runtime.png new file mode 100644 index 000000000000..6ff90e8cc0f0 Binary files /dev/null and b/docs/coding/images/Runtime_Viewer_View_Runtime.png differ diff --git a/docs/coding/images/debug_addtimer.png b/docs/coding/images/debug_addtimer.png new file mode 100644 index 000000000000..4d7e29e93f61 Binary files /dev/null and b/docs/coding/images/debug_addtimer.png differ diff --git a/docs/coding/images/debug_arg_vars.png b/docs/coding/images/debug_arg_vars.png new file mode 100644 index 000000000000..cc881635d3df Binary files /dev/null and b/docs/coding/images/debug_arg_vars.png differ diff --git a/docs/coding/images/debug_assembly_def.png b/docs/coding/images/debug_assembly_def.png new file mode 100644 index 000000000000..1017b0257229 Binary files /dev/null and b/docs/coding/images/debug_assembly_def.png differ diff --git a/docs/coding/images/debug_call_stack.png b/docs/coding/images/debug_call_stack.png new file mode 100644 index 000000000000..6a904edb9198 Binary files /dev/null and b/docs/coding/images/debug_call_stack.png differ diff --git a/docs/coding/images/debug_check_turf.png b/docs/coding/images/debug_check_turf.png new file mode 100644 index 000000000000..f7c21e607fa8 Binary files /dev/null and b/docs/coding/images/debug_check_turf.png differ diff --git a/docs/coding/images/debug_common_logic.png b/docs/coding/images/debug_common_logic.png new file mode 100644 index 000000000000..dfed2e73fed4 Binary files /dev/null and b/docs/coding/images/debug_common_logic.png differ diff --git a/docs/coding/images/debug_complete_other_proc.png b/docs/coding/images/debug_complete_other_proc.png new file mode 100644 index 000000000000..125985532777 Binary files /dev/null and b/docs/coding/images/debug_complete_other_proc.png differ diff --git a/docs/coding/images/debug_game_paused.png b/docs/coding/images/debug_game_paused.png new file mode 100644 index 000000000000..0aee2157eeea Binary files /dev/null and b/docs/coding/images/debug_game_paused.png differ diff --git a/docs/coding/images/debug_get_outer_object.png b/docs/coding/images/debug_get_outer_object.png new file mode 100644 index 000000000000..51ac102787bf Binary files /dev/null and b/docs/coding/images/debug_get_outer_object.png differ diff --git a/docs/coding/images/debug_holder_var.png b/docs/coding/images/debug_holder_var.png new file mode 100644 index 000000000000..1afbef7c6ebd Binary files /dev/null and b/docs/coding/images/debug_holder_var.png differ diff --git a/docs/coding/images/debug_ibeam_findreferences.png b/docs/coding/images/debug_ibeam_findreferences.png new file mode 100644 index 000000000000..986be7054e1e Binary files /dev/null and b/docs/coding/images/debug_ibeam_findreferences.png differ diff --git a/docs/coding/images/debug_ibeam_findresults.png b/docs/coding/images/debug_ibeam_findresults.png new file mode 100644 index 000000000000..164cce28581a Binary files /dev/null and b/docs/coding/images/debug_ibeam_findresults.png differ diff --git a/docs/coding/images/debug_ibeam_hit.png b/docs/coding/images/debug_ibeam_hit.png new file mode 100644 index 000000000000..c848f6af8485 Binary files /dev/null and b/docs/coding/images/debug_ibeam_hit.png differ diff --git a/docs/coding/images/debug_infra_process.png b/docs/coding/images/debug_infra_process.png new file mode 100644 index 000000000000..7ee9f9d42bc5 Binary files /dev/null and b/docs/coding/images/debug_infra_process.png differ diff --git a/docs/coding/images/debug_infra_search_results.png b/docs/coding/images/debug_infra_search_results.png new file mode 100644 index 000000000000..f0fa44b233c1 Binary files /dev/null and b/docs/coding/images/debug_infra_search_results.png differ diff --git a/docs/coding/images/debug_lower_breakpoint.png b/docs/coding/images/debug_lower_breakpoint.png new file mode 100644 index 000000000000..fd938b501d31 Binary files /dev/null and b/docs/coding/images/debug_lower_breakpoint.png differ diff --git a/docs/coding/images/debug_mine_location.png b/docs/coding/images/debug_mine_location.png new file mode 100644 index 000000000000..4068468baa22 Binary files /dev/null and b/docs/coding/images/debug_mine_location.png differ diff --git a/docs/coding/images/debug_outer_object_breakpoint.png b/docs/coding/images/debug_outer_object_breakpoint.png new file mode 100644 index 000000000000..425f2ce93c59 Binary files /dev/null and b/docs/coding/images/debug_outer_object_breakpoint.png differ diff --git a/docs/coding/images/debug_pulse_false.png b/docs/coding/images/debug_pulse_false.png new file mode 100644 index 000000000000..d20fdd03ad5a Binary files /dev/null and b/docs/coding/images/debug_pulse_false.png differ diff --git a/docs/coding/images/debug_radio_pulse.png b/docs/coding/images/debug_radio_pulse.png new file mode 100644 index 000000000000..390cf7f445fc Binary files /dev/null and b/docs/coding/images/debug_radio_pulse.png differ diff --git a/docs/coding/images/debug_runtime.png b/docs/coding/images/debug_runtime.png new file mode 100644 index 000000000000..ade35c432d63 Binary files /dev/null and b/docs/coding/images/debug_runtime.png differ diff --git a/docs/coding/images/debug_set_breakpoint.png b/docs/coding/images/debug_set_breakpoint.png new file mode 100644 index 000000000000..d2c183ca6ca0 Binary files /dev/null and b/docs/coding/images/debug_set_breakpoint.png differ diff --git a/docs/coding/images/debug_step_into_button.png b/docs/coding/images/debug_step_into_button.png new file mode 100644 index 000000000000..a7f398ef8ec1 Binary files /dev/null and b/docs/coding/images/debug_step_into_button.png differ diff --git a/docs/coding/images/debug_stepthrough.png b/docs/coding/images/debug_stepthrough.png new file mode 100644 index 000000000000..8f38dfb263d9 Binary files /dev/null and b/docs/coding/images/debug_stepthrough.png differ diff --git a/docs/coding/images/debug_stepthrough_create.png b/docs/coding/images/debug_stepthrough_create.png new file mode 100644 index 000000000000..ba8fb98a3269 Binary files /dev/null and b/docs/coding/images/debug_stepthrough_create.png differ diff --git a/docs/coding/images/debug_stepthrough_getturf.png b/docs/coding/images/debug_stepthrough_getturf.png new file mode 100644 index 000000000000..33c8655a4f92 Binary files /dev/null and b/docs/coding/images/debug_stepthrough_getturf.png differ diff --git a/docs/coding/images/debug_top_call_stack.png b/docs/coding/images/debug_top_call_stack.png new file mode 100644 index 000000000000..669c9e89d2cd Binary files /dev/null and b/docs/coding/images/debug_top_call_stack.png differ diff --git a/docs/coding/images/debug_trigger_proc.png b/docs/coding/images/debug_trigger_proc.png new file mode 100644 index 000000000000..990c7d0282c6 Binary files /dev/null and b/docs/coding/images/debug_trigger_proc.png differ diff --git a/docs/coding/images/for_loop_timing.png b/docs/coding/images/for_loop_timing.png new file mode 100644 index 000000000000..02b755d4772c Binary files /dev/null and b/docs/coding/images/for_loop_timing.png differ diff --git a/docs/coding/images/quickstart_animal_gib.png b/docs/coding/images/quickstart_animal_gib.png new file mode 100644 index 000000000000..081900f6e508 Binary files /dev/null and b/docs/coding/images/quickstart_animal_gib.png differ diff --git a/docs/coding/images/quickstart_build_errors.png b/docs/coding/images/quickstart_build_errors.png new file mode 100644 index 000000000000..a3acee5091a7 Binary files /dev/null and b/docs/coding/images/quickstart_build_errors.png differ diff --git a/docs/coding/images/quickstart_checkout_to.png b/docs/coding/images/quickstart_checkout_to.png new file mode 100644 index 000000000000..8b7a2e70199e Binary files /dev/null and b/docs/coding/images/quickstart_checkout_to.png differ diff --git a/docs/coding/images/quickstart_commit_message.png b/docs/coding/images/quickstart_commit_message.png new file mode 100644 index 000000000000..0c720d2cfc0e Binary files /dev/null and b/docs/coding/images/quickstart_commit_message.png differ diff --git a/docs/coding/images/quickstart_ctrl_key.png b/docs/coding/images/quickstart_ctrl_key.png new file mode 100644 index 000000000000..d606cd9d535f Binary files /dev/null and b/docs/coding/images/quickstart_ctrl_key.png differ diff --git a/docs/coding/images/quickstart_folder_navigate.png b/docs/coding/images/quickstart_folder_navigate.png new file mode 100644 index 000000000000..fb0c5d1e3b3b Binary files /dev/null and b/docs/coding/images/quickstart_folder_navigate.png differ diff --git a/docs/coding/images/quickstart_gib_search.png b/docs/coding/images/quickstart_gib_search.png new file mode 100644 index 000000000000..3780f2b9d9ac Binary files /dev/null and b/docs/coding/images/quickstart_gib_search.png differ diff --git a/docs/coding/images/quickstart_good_first_issue.png b/docs/coding/images/quickstart_good_first_issue.png new file mode 100644 index 000000000000..2680e5076130 Binary files /dev/null and b/docs/coding/images/quickstart_good_first_issue.png differ diff --git a/docs/coding/images/quickstart_mailman_search.png b/docs/coding/images/quickstart_mailman_search.png new file mode 100644 index 000000000000..bdfd955998b8 Binary files /dev/null and b/docs/coding/images/quickstart_mailman_search.png differ diff --git a/docs/coding/images/quickstart_new_branch_name.png b/docs/coding/images/quickstart_new_branch_name.png new file mode 100644 index 000000000000..da9fe0adeb04 Binary files /dev/null and b/docs/coding/images/quickstart_new_branch_name.png differ diff --git a/docs/coding/images/quickstart_problems_tab.png b/docs/coding/images/quickstart_problems_tab.png new file mode 100644 index 000000000000..3a55f4e5397c Binary files /dev/null and b/docs/coding/images/quickstart_problems_tab.png differ diff --git a/docs/coding/images/quickstart_push_branch.png b/docs/coding/images/quickstart_push_branch.png new file mode 100644 index 000000000000..d537f622f598 Binary files /dev/null and b/docs/coding/images/quickstart_push_branch.png differ diff --git a/docs/coding/images/quickstart_recent_pushes.png b/docs/coding/images/quickstart_recent_pushes.png new file mode 100644 index 000000000000..a6ac16a4f552 Binary files /dev/null and b/docs/coding/images/quickstart_recent_pushes.png differ diff --git a/docs/coding/images/quickstart_search_dm.png b/docs/coding/images/quickstart_search_dm.png new file mode 100644 index 000000000000..1237d58113c4 Binary files /dev/null and b/docs/coding/images/quickstart_search_dm.png differ diff --git a/docs/coding/images/quickstart_search_multitool.png b/docs/coding/images/quickstart_search_multitool.png new file mode 100644 index 000000000000..8561af196aa9 Binary files /dev/null and b/docs/coding/images/quickstart_search_multitool.png differ diff --git a/docs/coding/images/quickstart_search_multitool_quotes.png b/docs/coding/images/quickstart_search_multitool_quotes.png new file mode 100644 index 000000000000..70bbf48726f7 Binary files /dev/null and b/docs/coding/images/quickstart_search_multitool_quotes.png differ diff --git a/docs/coding/images/quickstart_search_toolhelpers.png b/docs/coding/images/quickstart_search_toolhelpers.png new file mode 100644 index 000000000000..1c49f804b6c3 Binary files /dev/null and b/docs/coding/images/quickstart_search_toolhelpers.png differ diff --git a/docs/coding/images/quickstart_vsc_tasks_config.png b/docs/coding/images/quickstart_vsc_tasks_config.png new file mode 100644 index 000000000000..cf391d2e5b0f Binary files /dev/null and b/docs/coding/images/quickstart_vsc_tasks_config.png differ diff --git a/docs/coding/images/quickstart_vsc_whitespace_setting.png b/docs/coding/images/quickstart_vsc_whitespace_setting.png new file mode 100644 index 000000000000..7057ab1afc69 Binary files /dev/null and b/docs/coding/images/quickstart_vsc_whitespace_setting.png differ diff --git a/docs/coding/images/quickstart_yellow_underline.png b/docs/coding/images/quickstart_yellow_underline.png new file mode 100644 index 000000000000..eff179d5c749 Binary files /dev/null and b/docs/coding/images/quickstart_yellow_underline.png differ diff --git a/docs/coding/quickstart.md b/docs/coding/quickstart.md new file mode 100644 index 000000000000..b22fd6c9187c --- /dev/null +++ b/docs/coding/quickstart.md @@ -0,0 +1,519 @@ +# Code Contribution Quickstart + +by *Farie82* + +## Intro +Glad you're reading this and hopefully this guide will help you start +contributing to this codebase! First a word of wisdom. Start small with your +first set of PRs even if you are already experienced with developing for other +languages or codebases. Every codebase has its own quirks and standards which +you will discover by doing and receiving feedback on your work. This guide will +help you set up your git and make your first PR. It will also include some tips +on how to (in my opinion) best handle the codebase. This guide will assume that +you have at least (very) minor knowledge of how programming works. Knowing what +a `string` is and how `if` statements work for example. The guide will also +assume that you will use VS Code which the [Getting +Started](../contributing/getting_started.md) guide helps you set up. + +Be sure to also take a look at the [contributing page](../CONTRIBUTING.md) so +you know what the coding standards are here. + +I've also made a [debugging tutorial](./debugging.md) which will help you find +the cause of bugs and how to fix them. + +## Quick DM tutorial +For your first PR you won't need an in-depth knowledge of how to code in DM, but +here are some of the basics. Feel free to skip these and come back to this once +you feel like you are missing some info. + +The [DM reference guide](http://www.byond.com/docs/ref/) is also great when you +want to look up how a proc or such works. VS Code does have a build-in reference +guide for you to use as well. Just `Ctrl`-click on any BYOND proc or variable to +see the reference on it. + +### Objects and Inheritance +An object is defined the following way: +```dm +/obj/item/multitool +``` + +Here we can see a `multitool` being defined. A `multitool` is an `item` which is +an `obj`. This is how the class inheritance works for DM. A real-life example is +that a dog is an animal and a cat is an animal. But a dog is not a cat. In DM it +could look something like this: + +```dm +/mob/animal/cat + name = "Cat" + +/mob/animal/dog + name = "Dog" +``` + +Where `mob` is a being in DM. Thus something that "lives" and can do things. + +### Procs +The way DM groups a set of instructions is as follows. It uses a *proc* or in +other languages also called a method or function. + +```dm +/obj/item/pen/multi/proc/select_colour(mob/user) + var/newcolour = input(user, "Which colour would you like to use?", name, colour) as null|anything in colour_choices + if(newcolour) + colour = newcolour + playsound(loc, 'sound/effects/pop.ogg', 50, 1) + update_icon() +``` + +`/obj/item/pen/multi/proc/select_colour` here is the proc definition. Meaning +this is the first instance of this proc. For this, you need to add `proc/` in +front of the method name (`select_colour` in this case). `mob/user` is here a +parameter given to the proc. The name of the parameter is `user` and its type is +`mob`. + +As with other languages you can also override the behaviour of a proc. + +```dm +/obj/item/pen/multi/attack_self(mob/living/user) + select_colour(user) +``` + +Here the proc `attack_self` is overridden with new behaviour. It will call +`select_colour` with as a parameter the given `user`. + +#### Overriding Procs + +When overriding a proc you can also call the parent's implementation. This is +especially handy when you want to extend the existing behaviour with new +behaviour. + +```dm +/obj/item/pen/multi/Initialize(mapload) + . = ..() + update_icon() +``` + +Here `Initialize` is overridden with `mapload` as a parameter. `..()` means call +the parent implementation of this proc with the parameters given to this +version. So `mapload` will be passed through. `. = ..()` means assign the value +that the parent's version returns as our default return value. `.` is the +default return value in DM. So if you don't return an explicit value at the end +of the proc then `.` will be returned. + +```dm +/proc/test() + . = "Yes" + return "No" +``` + +This will return `"No"` since you explicitly state to return `"No"`. + +Small tip. You can also `Ctrl`-click on `..()` to go to the parent's definition. + +### Putting values easily in strings + +Other languages use something like `string.format("{0} says {1}", mob_name, +say_text)`. But DM has something nifty for that. The same result can be achieved +in DM using the following: + +```dm +"[mob_name] says [say_text]" +``` + +`[...]` will run the code and return the outcome inside the `[]`. In the case +above it will just return the value of the variables but you can also use logic +here. + +```dm +var/val = 1 +world.log << "val is [val]. val plus 10 is: [val + 10]" +``` + +Which will produce `"val is 1. val plus 10 is: 11"` + +### Scoping +If you come from another language then you might think. "Hey, where are the +{}'s?!". Well, we do not use those (usually). Instead scoping is done by +whitespace. Tabs in our case. One tab means one scope deeper. + +```dm +/mob + name = "Thing" + +/mob/proc/test() + world.log << name // We can access name here since we are in the mob + if(name == "Thing") + var/value = 10 + world.log << "[value]" // We can also access value here since it is in the same scope or higher as us. + world.log << "Will only happen if name is Thing" + else + world.log << "Will only happen if name is not Thing" + world.log << "Will always happen even if name is not Thing" + world.log << "[value]" // This will produce an error since value is not defined in our current scope or higher +``` + +In VS Code you can make your life easier by turning on the rendering of +whitespace. Go to the settings and search for whitespace. + +![image](./images/quickstart_vsc_whitespace_setting.png) + +I have set it up so that I can only see the boundary whitespace. Meaning that I +visually see spaces and tabs on the outmost left and right side of a line. Very +handy in spotting indentation errors. + +### Deleting stuff + +DM has a build-in proc called `del`. **DO NOT USE THIS**. `del` is very slow and +gives us no control over properly destroying the object. Instead, most/all SS13 +codebases have made their own version for this. `qdel` which will queue a delete +for a given item. You should always call `qdel` when deleting an object. This +will not only be better performance-wise but it will also ensure that other +objects get notified about its deletion if needed. + +### Coding Standards + +**Before you start coding it is best to read our** [contributing page](../CONTRIBUTING.md). +It contains all of the coding standards and some tips and tricks on how to write +good and safe code. + +### Terminology +We will be using some terminology moving forward you should be comfortable with: + +- [PR](../references/glossary.md#pull-request), an abbreviation for pull + request. This is the thing that will get your changes into the actual game. In + short, it will say you request certain changes to be approved and merged into + the master branch. Which is then used to run the actual game. + +- [VS Code](../references/glossary.md#vsc), short for Visual Studio Code. The + place where you do all your magic. It is both a text editor with a lot of + helpful tools and a place where you can run and debug your code. + +- Scoping; defining what code belongs to what. You don't want to make everything + public to the whole codebase so you use scoping. See the explanation above for + more info. + +- Feature branch; the branch where your new feature or fix is located on. Git + works with branches. Each branch containing a different version of the + codebase. When making a PR git will look at the differences between your + branch and the master branch. + +## Setup +Code contributions require setting up a development environment. If you haven't +done that already, follow the guide at [Getting Started](../contributing/getting_started.md) +first. + +## Your First PR +Once you've completed the setup you can continue with making an actual PR. + +I'd suggest keeping it small since setting up all of the git stuff was already a +task of its own. My suggestion would be to look at issues with the [Good First +Issue][gfi] label. These usually are considered to be easy to solve. Usually, +they will also contain some comments containing hints on how to solve them. When +picking one be sure that you do not pick an issue that already has an open PR +attached to it like in the picture below. + +![image](./images/quickstart_good_first_issue.png) + +You *can* make a PR that solves that issue. But it would be a waste of time +since somebody else already solved it before you but their PR is still awaiting +approval. + +If there are no suitable good first issues then you can look through the issue +list yourself to find some. Good ones include typos or small logic errors. If +you know of any issues that are not yet listed in the issues list then those are +also fine candidates. + +Alternatively, you can implement a small new feature or change. Good examples +include: + +- More or changed flavour text to an item/ability etc. +- Adding (existing) sound effects to abilities/actions. +- Adding administrative logging where it is missing. For example, a martial arts + combo not being logged. +- A new set of clothing or a simple item. + +There of course are many more options that are not included in this list. + +[gfi]: https://github.com/ParadiseSS13/Paradise/labels/Good%20First%20Issue + +### Finding The Relevant Code +The first thing you will need to do is to find the relevant code once you +figured out what you want to add/change. This is no exact science and requires +some creative thinking but I will list a few methods I use myself when finding +code. + +For all of these, you will need to have VS code open and use the search +functionality. I tend to only look for things in dm files. Which are the code +files. + +![image](./images/quickstart_search_dm.png) + +#### Finding Existing Items +If you're looking for an existing item then it might be easiest to look for the +name of the item. Let's take a multitool as an example here. + +When looking for the term `multitool` you will tend to find a lot of results. +305 results on my current version of the game in fact. + +![image](./images/quickstart_search_multitool.png) + +Alternatively, you can search for `"multitool"` (the string with the value +`multitool`) and find a lot fewer results. For demonstration purposes, I will +exclude the `""` here. This will give you the following match: + +![image](./images/quickstart_search_multitool_quotes.png) + +This might seem like a lot (and it is) but you don't have to go through them all +to find the item itself. Using some deduction we can find the result we need. Or +find it via another result we found. Here we can see that there are some matches +with `obj/item/multitool` for example: + +![image](./images/quickstart_search_toolhelpers.png) + +We know that we are looking for a multitool and that a multitool is an item so +it looks like this is what we want to find. When hovering over the `multitool` +part of `obj/item/multitool` and holding the `Ctrl` key you will see the +definition of the object. + +![image](./images/quickstart_ctrl_key.png) + +Perfect! This is the one we need. How how do we get to that definition? Simple +you click on `multitool` when holding `Ctrl`. This will send you to the definition +of the object. + +Most of the times the file containing the definition will also include the +looked for proc or value you want to change. + +#### Finding Behaviour +This is a very wide concept and thus hard to exactly find. + +For this, we will use the above method and use keywords explaining the behaviour +you want to search. For example a mob gibbing. Simply looking for `gib` here +will find us too many results. About 1073 in my case. Instead, we will try to +look for a proc named gib. `/gib(` will be used as our search criteria here. + +![image](./images/quickstart_gib_search.png) + +Et voila, just 15 results. + +Say we want to delete the pet collar of animals such as Ian when he is gibbed. +Here we need to find something stating that the `gib` belongs to an animal. + +![image](./images/quickstart_animal_gib.png) + +`/mob/living/simple_animal/gib()` is what we are looking for here. Ian is an +animal. `simple_animal` in code. + +This will find us the following code (on my current branch): + +```dm +/mob/living/simple_animal/gib() + if(icon_gib) + flick(icon_gib, src) + if(butcher_results) + var/atom/Tsec = drop_location() + for(var/path in butcher_results) + for(var/i in 1 to butcher_results[path]) + new path(Tsec) + if(pcollar) + pcollar.forceMove(drop_location()) + pcollar = null + ..() +``` + +The behaviour we're looking for here has to do with the `pcollar` code there. It +will currently move the attached pet collar (if any) to the drop location of the +animal when they are gibbed. + +#### Finding A Suitable Place To Add A New Item +When adding a new item you want to ensure that it is placed in a logical file or +that you make a new file in a logical directory. I find that it is best to find +other similar items and see how they are defined. For example a special jumpsuit +without armour values. Here we first go look for the existing non-job-related +jumpsuits such as the `"mailman's jumpsuit"`. Say we don't know the exact name +of that jumpsuit but we do know that it is for a mailman. + +Our best bet will be to look for the term `mailman` and see what pops up. This +is a rather uncommon term so it should give only a few results. + +![image](./images/quickstart_mailman_search.png) + +Perfect. Even the item definition has the name mailman in it. + +We already see from the search results that the item is defined in the +`miscellaneous.dm` file. Navigating to it will show us the directory it is in. + +![image](./images/quickstart_folder_navigate.png) + +As you can see most clothing items are defined in this `clothing` directory. +Depending on your to add the item you can pick one of those files and see if it +would fit in there. Feel free to ask for advice from others if you are unsure. + +### Solving The Actual Issue +Now comes the **Fun** part. How to achieve what you want to achieve? The answer +is. "That depends" Fun, isn't it? Every problem has its own way of solving it. I +will list some of the more common solutions to a problem down here. This list +will of course not be complete. + +A great hotkey for building your code quick is `Ctrl` + `Shift` + `B`. Then press enter to +select to build via Byond. This will start building your code in the console at +the bottom of your screen (by default). It will also show any errors in the +build process. + +![image](./images/quickstart_build_errors.png) + +Here I have "accidentally" placed some text where it should not belong. Going to +the "Problems" tab and clicking on the error will bring you to where it goes +wrong. + +![image](./images/quickstart_problems_tab.png) + +This error does not tell much on its own (Byond is not great at telling you what +goes wrong sometimes) but going to the location shows the problem quite easily. + +![image](./images/quickstart_yellow_underline.png) + +More cases might be added later. + +#### Typo Or Grammar +The easiest of them all if you can properly speak English. Say the multitool +description text is: `"Used for pusling wires to test which to cut. Not +recommended by doctors."` Then you can easily fix the typo ("pusling" to +"pulsing") by just changing the value of the string to the correct spelling. + +#### Wrong Logic +This one really depends on the context. But let us take the following example. +You cannot link machinery using a multitool. Something it should do. + +```dm +/obj/item/multitool/proc/set_multitool_buffer(mob/user, obj/machinery/M) + if(ismachinery(M)) + to_chat(user, "That's not a machine!") + return +``` + +Here a simple mistake is made. A `!` is forgotten. `!` will negate the outcome +of any given value. In this case, a check to see if `M` is indeed a piece of +machinery. This seems dumb to forget or do wrong but it can happen when a large +PR gets made and is changed often. Testing every case is difficult and cases can +slip under the radar. + +#### Adding A New Item + +You can start defining the new item once you found the proper file the item +should belong to. Depending on the item you will have to write different code +(duh). We will take the new jumpsuit as an example again and will put it in the +`miscellaneous` file. + +When defining a new jumpsuit you can easily copy an existing one and change the +definition values. We will take the mailman outfit as a template. + +```dm +/obj/item/clothing/under/rank/mailman + name = "mailman's jumpsuit" + desc = "'Special delivery!'" + icon_state = "mailman" + item_state = "b_suit" + item_color = "mailman" +``` + +As seen here a jumpsuit has multiple values you can define. The `name` is pretty +straight forward. `desc` is the description of the item. `icon_state` is the +name the sprite has in the DMI file. `item_state` is the name of the sprite of +the suit while held in your hands has in the DMI file. `item_color` is the name +of the sprite in the `icons/mob/uniform.dmi` file. This value will indicate what +sprite will be used when a person wears this jumpsuit. (I know `item_color` is a +weird name for this) + +We of course also have to change the path of the newly created object. We'll +name it `/obj/item/clothing/under/rank/tutorial`. This alone will make it so +that you can spawn the item using admin powers. It will not automagically appear +in vendors or such. + +### Testing Your Code +Once you are done coding you can start testing your code, assuming your code compiles of course. + +To do this simply press `F5` on your keyboard. This will by default build your +code and start the game with a debugger attached. This allows you to debug the +code in more detail. + +Later I will include a more detailed testing plan in this guide. + +### Making The PR +Once you are done with your changes you can make a new PR. + +New PRs must be created on _branches_. Branches are copies of the `master` +branch that constitutes the server codebase. Making a separate branch for each +PR ensures your `master` branch remains clean and can pull in changes from +upstream easily. + +![image](./images/quickstart_checkout_to.png) + +Select "Create new branch" and then give your new branch a name in the top text +bar in VS code. Press enter once you are done and you will have created a new +branch. Your saved changes will be carried over to the new branch. + +![image](./images/quickstart_new_branch_name.png) + +You are ready to commit the changes once you are on your feature branch and when +the code is done and tested. Simply write a (short) useful commit message +explaining what you've done. For example when implementing the pet collar +gibbing example + +![image](./images/quickstart_commit_message.png) + +Here you can also see the changes you have made. Clicking on a file will show +you the difference between how it was before and how it is now. When you are +happy with the existing changes you can press the commit button. The checkmark. +By default it will commit all the changes there are unless you staged changes. +Then it will only commit the staged changes. Handy when you want to commit some +code while you have some experimental code still there. + +Once it is committed you will have to also publish the branch. + +![image](./images/quickstart_push_branch.png) + +When pressing push it will ask you if you want to publish the branch since there +is no known remote version of this yet (not on Github). Say yes to that and +select "origin" from the list of available remotes. Now your code is safely +pushed to Github. + +Now go to the Github page of this codebase and you will see the following: + +![image](./images/quickstart_recent_pushes.png) + +Click on the green button and Github will auto-create a PR template for you for +the branch you just pushed. Be sure to fill in the template shown to you. It is +quite straight forward but it is important to note down the thing you changed in +enough detail. If you fixed an issue then you can write the following `fixes #12345` +where 12345 is the number of the issue. Put this line of text in the +*What Does This PR Do* part of the PR. + +You can submit it once you are happy with how the PR looks. After this, your PR +will be made public and visible to others to review. In due time a maintainer +will look at your PR and will merge it if it is deemed an addition to the +codebase. + +## Tips And Tricks +Here I will list some of my tips and tricks that can be useful for you when +developing our codebase. + +- Sounds like a simple and logical one. But always feel free to ask others for + help/advice. This project is an open-source project run by a lot of passionate + people who want to improve the codebase together. Asking for help/advice will + not only help you get your code running but will also show you are interested + and will help you improve your skills. + +- Learn the VS Code shortcuts. This really saves you a lot of time. `Ctrl` + `Shift` + `B` + will build your project. `F5` will run it in debug mode. `Ctrl` + `Shift` + `F` + will global search. `Ctrl` + `P` will find definitions of things (super handy). + +- Use find references when right-clicking on a variable or proc. This will list + all the uses of this var/proc so you can see what it is actually used for or + where you need to change things as well etc. + +- You can remove the build command from F5. This saves you some time when you + develop. Be sure to manually build your code though! This can be done by + removing the `preLaunchTask` line in the debug config: + +![image](./images/quickstart_vsc_tasks_config.png) diff --git a/docs/coding/style_guidelines.md b/docs/coding/style_guidelines.md new file mode 100644 index 000000000000..f95cf196def6 --- /dev/null +++ b/docs/coding/style_guidelines.md @@ -0,0 +1,345 @@ +# Style Guidelines + +These guidelines are designed to maintain readability and establish a standard +for future contributions. By following these guidelines, we can reduce the +overhead during the review process and pave the way for future content, fixes, +and more. + +## Variables + +Variable conventions and naming are an important part of the development +process. We have a few rules for variable naming, some dictated by BYOND itself. +While naming variables can be tough, we ask that variable names are descriptive. +This helps contributors of all different levels understand the code better. +These guidelines only apply to DM, as TGUI uses a different convention. Avoid +using single-letter variables. Variable naming is to follow American English +spelling of words. This means that variables using British English will be +rejected. This is to maintain consistency with BYOND. + +Variables written in DM require the use of snake_case, which means words will be +spaced by an underscore while remaining lowercase. + +```dm +// An example of a variable written in snake_case +var/example_variable +``` + +## Strings and Messages + +### Strings + +When it comes to strings, they should be enclosed in double quotations. Like the +naming convention for variables, American English spelling is to be used. + +```dm +var/example_string = "An example of a properly formatted string!" +``` + +If a string is too long, break it into smaller parts for better readability. +This is especially useful for long descriptions or sentences, making the text +easier to read and understand. + +```dm +var/example_long_string = "This is a longer than average string \ + and how it should be formatted. This \ + is the method we prefer!" +``` + +Variables may be incorporated within strings to dynamically convey their values. +This practice is beneficial when the variable's value may change, enhancing +flexibility and maintainability. Avoid hardcoding values directly into strings, +as it is considered poor practice and can lead to less adaptable code. + +```dm +// Bad +var/bad_example_string = "There are 20 items in the box." + +// Good +var/item_count = 20 +var/good_example_string = "There are [item_count] items in the box." +``` + +### Messages + +Messages are anything that is sent to the chat window. This can include system +messages, messages to the user, as well as messages between users. + +#### Sending to chat + +Though there are multiple ways to send a message to the chat window, only +certain methods will be accepted. Avoid using `<<` when sending information to +the chat window. You can check out other examples throughout the codebase to see +how messages are typically handled. + +```dm +// Bad + world << "Hello World!" + + // Good + to_chat(world, "Hello World!") + +// Also Good +user.visible_message( + "[user] writes Hello World!", + "You write Hello World!.", + "You hear someone writing Hello World!." +) +``` + +#### Common Classes + +- ``'notice'``: used to convey anything the player should be aware of, including + actions, successes, and other pertinent information that is non-threatening. + This also includes information directly unrelated to gameplay. +- ``'warning'``: used for failures, errors, and warnings +- ``'danger'``: danger occurring around the player or to other players, damage + to things around the player +- ``'userdanger'``: used to convey to the player that they are being attacked or + damaged + +These can be set using in-line span tags. + +```dm +proc/my_example_proc + to_chat(user, "Message with the notice style.") +``` + +You are not limited to the styles listed above. It is important, however, to +evaluate and choose the right style accordingly. You can find additional styles +located within the chat style sheets. + +## Comments + +Comments are essential for documenting your code. They help others understand +what the code does by explaining its behavior and providing useful details. Use +comments where needed, even if the code seems clear. Proper commenting keeps the +codebase organized and provides valuable context for development. + +### Single-Line Comments + +Single-line comments are used for brief explanations or notes about the code. +They provide quick, straightforward context to help clarify the code’s purpose +or functionality. + +```dm +// This is a single-line comment +``` + +### Multi-Line Comments + +Used for longer explanations or comments spanning multiple lines. Good for +documenting parameters of procs. + +```dm +/* + * This is a multi-line comment. + * It spans multiple lines and provides detailed explanations. + */ +``` + +### Autodoc Comments + +[Autodoc][] is used for documenting variables, procs, and other elements that +require additional clarification. This is a useful tool, as it allows a coder to +see additional information about a variable or proc without having to navigate +to its declaration. To apply properly, an Autodoc comment should be used +**BEFORE** the actual declaration of a variable or proc. + +```dm +/// This is an Autodoc example +var/example_variable = TRUE +``` + +[Autodoc]: ../references/autodoc.md + +### Define Comments + +When documenting single-line macros such as constants, use the "enclosing +comment format", `//!`. This prevents issues with macro expansion: + +```dm +#define BLUE_KEY 90 //! The access code for the blue key. +``` + +These constant names can then be referred to in other Autodoc comments by +enclosing their names in brackets: + +```dm +/// This door only opens if your access is [BLUE_KEY]. +/obj/door/blue + ... +``` + +### Mark Comments + +Used to delineate distinct sections within a file when necessary. It should only +be used for that purpose. Avoid using it for items, procs, or datums. + +```dm +// MARK: [Section Name] +``` + +### Commented Out Code + +Commented out code is generally not permitted within the code base unless it is +used for the purpose of debugging. Code that is commented out during a +contribution should be removed prior to creating a pull request. If you are +unsure whether or not something should be left commented out, please contact a +development team member. + +## Multi-Line Procs and List Formatting + +When calling procs with very long arguments (such as list or proc definitions +with multiple or especially dense arguments), it may sometimes be preferable to +spread them out across multiple lines for clarity. For some more text-heavy +procs where readability of the arguments is especially important (such as +visible_message), you're asked to always multi-line them if you're providing +multiple arguments. + +For lists that may be subject to frequent code churn, we suggest adding a +trailing comma as well, as it prevents the line from needing to be changed +unnecessarily down the line. + +```dm +// Bad +var/list/numbers = list( + 1, 2, 3) + +user.visible_message("[user] writes the style guide.", +"You wonder if you're following the guide correctly.") + +// Good +var/list/numbers = list( + 1, + 2, + 3, +) + +user.visible_message( + "[user] writes the style guide.", + "You write the style guide.", + "You hear typing." +) + +// Also good +var/list/letters = list("a", "b", "c") +``` + +## Indentation + +Indentation in DM is used to define code blocks and scopes. Our code base +requiress tab spacing. Singular spacing to the length of four spaces will not be +accepted. + +```dm +// Good +for(var/example in 1 to 10) + if(example > 5) + to_chat(world, "Higher than five") + else + to_chat(world, "Lower than five") +``` + +Only when it comes to defines, curly braces can be used. This is allowed in some +instances to keep code neat and readable, and to ensure that macros expand +properly regardless of their indentation level in code + +```dm +// Good +#define FOO /datum/foo {\ + var/name = "my_foo"} +``` + +Not only is it easier to write, but the DM compiler also optimizes the preferred +method to run faster than the bad example. Using the DM style loop enhances +readability and aligns with the language’s conventions. + +## Operators + +### Spacing + +Code readability is an important aspect of developing on a large-scale project, +especially when it comes to open-source. As emphasized by other places in this +document, it is important to keep the code as readable as possible. One way we +do that is through spacing. Maintain a single space between all operators and +operands, including during variable declarations and value assignments. + +```dm +// Bad +var/example_variable=5 + +// Also Bad +example_variable=example_variable*2 + +// Good +var/example_variable = 5 + +// Also Good +example_variable = example_variable * 2 +``` + +## Boolean Defines + +Use `TRUE` and `FALSE` instead of 1 and 0 for booleans. This improves +readability and clarity, making it clear that values represent true or false +conditions. + +```dm +// Bad +var/example = 1 + if(example) + example_proc() + +// Good +var/example = TRUE + if(example) + example_proc() +``` + +## File Naming and References + +### Naming + +When naming files, it is important to keep readability in mind. Use these +guidelines when creating a new file. + +- Keep names short (≤ 25 characters). +- Do not add spaces. Use underscores to separate words. +- Do not include special characters such as: " / \ [ ] : ; | = , < ? > & $ # ! ' { } *. + +### References + +When referencing files, use single quotes (') around the file name instead of +double quotes. + +```dm +// Bad +var/sound_effect = "sounds/machines/wooden_closet_open.ogg" + +// Good +var/sound_effect = 'sounds/machines/wooden_closet_open.ogg' +``` + +## HTML Tag Format + +Though uppercase or mixing cases will work, we prefer to follow the W3 standard +for writing HTML tags. This means tags should be written in lowercase. This +makes code more readable and it just looks better. + +```dm +// Bad +This is an example of how not to do it. + +// Good +This is an example of how it should be. +``` + +## A Final Note + +These guidelines are subject to change, and this document may be expanded on in +the future. Contributors and reviewers should take note of it and reference it +when needed. By following these guidelines, we can promote consistency across +the codebase and improve the quality of our code. Not only that, by doing so, +you help reduce the workload for those responsible for reviewing and managing +your intended changes. Thank you for taking the time to review this document. +Happy contributing! diff --git a/docs/coding/testing_guide.md b/docs/coding/testing_guide.md new file mode 100644 index 000000000000..a87d8bb10b75 --- /dev/null +++ b/docs/coding/testing_guide.md @@ -0,0 +1,225 @@ +# Guide to Testing + +Code by nature works *as coded* and not always *as intended*, while you +know that your code compiles and passes tests on your Pull Request you +may not know if it breaks in edge cases or works fully in-game. In order +to ensure your changes actually work, you will need to know how to +**Test Your Code**. As part of this process, you will learn how to use +various in-game debugging tools to fully utilize your changes in a test +server, analyse variables at run-time, test for edge cases, and stress +test features. This guide will also explain more advanced concepts and +testing such as advanced proc calls, garbage collection testing, and +breakpoints. + +## Prerequisites + +- You will need to first [set up your development environment][setup] and + successfully launch a local server. +- Give yourself host-level permissions on your local server (You should have + access to every verb and tab available in-game). +- Have an open and patient mindset. +- Approach the QA process as if you're asking yourself questions and + answering them by performing successful (or unsuccessful) tests. + +[setup]: ../contributing/getting_started.md + +### Prep Work + +In order to speed this up, especially for experienced devs, know what +you're looking for and write down a list (mental lists work as well) of +what you want to test. If you're only changing an attribute, list the +interactions that attribute has with other functions so you remember to +test each one. If you're adding a new atom, write down possible +interactions that atom may have with other relevant atoms (think parent +objects, tools, materials, machinery such as autholathes, antagonists). + +Make your tests atomic. i.e. don't try and test everything at once, +pick one specific thing (or closely related groups of things) to test +on. If your item affects other items you want to test, consider +restarting and using a clean round or properly cleaning up the test +area. + +### Basic In-Game Tools + +While mastery of these tools is not required, basic familiarity with +them will be paramount to proper testing. + +#### Game Panel + +The Game Panel is a small menu that allows the user to set the game mode +for the round or spawn in atoms (turfs, objects, mobs, etc). In order to +access the Game Panel, you will need to click the *Game Panel* verb +under the admin tab. + +![](./images/Game_Panel.png) + +The Game panel has five buttons: + +1. **Change Gamemode** will allow the user to set the round game mode. This is + only binding if the round has not started yet. + +2. **Create Object** allows the user to spawn in any object. The search bar will + return all type paths relevant to the search given. + +3. **Quick Create Object** allows the user to search for objects in a more + specific scope (only guns, only mechs, etc). + +4. **Create Turf** allows the user to change the turf they are directly over. + +5. **Create Mob** allows the user to spawn in a mob. + +The most important buttons are the four create buttons. By clicking on them you +can open up the game panel create menu. For beginners, there are five important +aspects of the game panel that you will need to know (the other inputs and +buttons are very sparsely used, and likely not needed in your case). + +1. The type path to search for, this input will tell the panel to query + for any typepath that contains the given string, so if you searched + for "book" it would return type paths such as + "machinery/bookBinder", "spellbook/mime/oneuse", or + "book/codex_gigas". Keep in mind this will return *all* type paths + with the given string, since the game panel is tied to your client + CPU usage, trying to search type paths with a query such as "item" + or "mob" will return thousands of results and likely freeze your + client for some time or crash it. +2. The number or amount of the element you want to spawn, if you were + spawning a book and typed in three, it would spawn three books. +3. Where this object will spawn, generally you will want the default + "On the floor below mob" or if you're a human, "in own mob's + hands". If you're specifically trying to spawn the element inside + another object, you can mark the object and use that option. +4. The list of type paths to select, you will need to click the + typepath to select it. Alternatively, if you want to spawn in + multiple types at once, you can click-drag up to 5 type paths and + spawn them all at once. +5. The button that spawns stuff with the parameters you gave the panel. + +![](./images/Game_Panel_Create.png) + +#### Runtime Viewer + +The runtime viewer interface is a menu that displays every runtime that +occurred during the current round. It is available by clicking the *View +Runtimes* verb under the "Debug" tab. + +![](./images/Runtime_Viewer.png) + +The runtime viewer displays a list of *almost* every runtime in a round, a few +unimportant or repeated runtimes are skipped. Essentially, runtimes are errors +that occur when the server is running (as compared to a build error that occurs +when attempting to compile). Clicking on a runtime will open up more details +about it. + +1. The runtime error. This will generally include information about the + type of error (null reference, bad proc calls, etc), what file it + occurred in, what line it occurred at, and information about the + proc it occurred in. Some errors will also include "call stacks" + or the procs called leading up to the error. +2. user VV button, will open the view variables panel on the mob that + caused the runtime +3. user PP button, will open the player panel on the mob that caused + the runtime +4. user follow button, will force the user to follow/orbit the mob that + caused the runtime +5. loc VV button, will open the view variables panel on the loc (turf + or thing that contains the object) of the object that caused the + runtime +6. loc jump button, will force the user to jump to the loc where the + runtime occurred. + +![](./images/Runtime_Viewer_View_Runtime.png) + +## Does it Even Work? + +The first step in testing is to see if your change spawns in/displays +*at all*. This part of testing focuses solely on finding out when and +where your changes break, not particularly how or why it breaks. + +If your change is creation/removal of an atom. open up the [Game Panel](#game-panel) +and see if the atom has been added/removed as a typepath. If it's not, make sure +your code was actually compiled and check to either see if you: + +- actually defined a new typepath properly and have the file ticked in the DME + file, and +- you removed ALL instances where the type path is used (even proc + definitions!). + +If your change is a map change, please see the [Mapping Requirements](../mapping/requirements.md). + +Use the game panel to spawn your atom with the given type path. Ensure the following: + +- Does it appear? +- Is the sprite correct? +- Is the name/appearance/description of the atom correct? + +_Note:_ An Atom in DM refers to all elements of type "area", "turf", "object", +or "mob." Each has different behaviors for spawning, deletion, and interaction, +so keep that in mind. Additionally, there will not be much reference/relevance +in this section to "Area" type atoms. + +### Does it Work the Way You Want it to? + +Test the attributes of your atom: + +- If it has health, can you kill or break it? +- If it has a menu, can you open up and interact with the UI correctly, can you + press buttons? +- If you added a special feature, can you activate it correctly? +- Does your new turf have proper atmospherics? + +You may not have touched a certain section of code, but it's entirely +possible that you broke it with a nearby change, check to make sure it +still works the way it's supposed to (or even at all). For example, if +you modified a variable inside a book object, can the barcode scanner +still scan it into the library system? If you changed the way xenomorphs +handle combat, will disablers, lasers, batons, etc still work the same +way or function at all? + +### Be Concise and Specific + +Your goal here is to break your change in every (relevant) way possible, +use your change in every way you intended it to be used and then use it +every way it wasn't intended to be used. However, this isn't to say +you need to test every use case or test every single object that may be +affected. As a contributor, you have limited time in your day to spend +on coding, don't waste all of it trying out every different testing +scenario. Here are a few tips to be efficient: + +- Know the code so that you know what other objects or parts of a feature will + be affected, then you have a mental list of things that need to be tested. +- Focus on testing one feature at a time, especially ones that you're focused on + coding at the moment; This keeps your attention scoped to that feature so you + can quickly make the needed changes and move on (this will help avoid + "*scope-creep*.") +- If your feature is built on another feature working (i.e. your feature working + *depends* completely on another feature working properly), test the dependency + if your feature is breaking to ensure the point of failure isn't just a + dependency breaking. + +## Why Doesn't it Work? + +The previous question of "does it work" often answers itself just by +spooling up a test environment, however, figuring out why things break +is a much more difficult and in-depth task. This section will avoid +getting into technical discussion and will instead explore conceptually +how to begin understanding why your change is not working. + +### Does it Produce Errors? + +Changes that clearly break often come saddled with a few *runtimes* which are +errors that occur while the server is actively running. In your preferences tab, +you can click the *Toggle Debug Log Messages* verb to toggle on debug message +which will allow you to see runtimes pop up exactly when they happen in the chat +box. You will need to do this every round unless you have a database properly +set up. Additionally, you can view all runtimes in a round by clicking the +[*View Runtimes*](#runtime-viewer) verb in the debug tab to open up the runtime +viewer. + +This will allow you to identify the errors your changes are producing +and possibly even identify where, how, and what is breaking in your +code. Do note that this will often not reveal larger issues with your +code that is sourced from bad design decisions or unintentional effects. + +## TODOs + +Further sections are forthcoming, including assertion checking. diff --git a/docs/coding/testing_requirements.md b/docs/coding/testing_requirements.md new file mode 100644 index 000000000000..9aa22633c354 --- /dev/null +++ b/docs/coding/testing_requirements.md @@ -0,0 +1,77 @@ +# Pull Request Testing Requirements + +Testing is a critical aspect of the pull request process for the development +here at Paradise. Bugs often arise due to insufficient testing, which +can compromise the hard work of our contributors and development team members. +It is mandatory that all pull requests undergo thorough testing before merging. +Failure to comply may result in closure of the pull request and possible +disciplinary action. + +## Testing Procedures + +### Local Testing + +1. Compile and Run: Ensure the code compiles without errors. + +2. Game Loading: Use your preferred debugging method to load the game and verify + changes are applied correctly. + +3. Functional Testing: Test new features or changes to ensure they integrate + smoothly with existing functionality. + +### Validation + +1. Feature Integrity: Confirm that new additions do not break existing game + features. + +2. Performance Testing: Assess the performance impact of changes to ensure they + meet acceptable standards. + +### Comprehensive Review + +1. Edge Cases: Test edge cases to ensure robustness of the code. + +2. Error Handling: Verify error handling mechanisms are effective and + informative. + +### Documentation and Reporting + +1. Update Documentation: If changes impact user-facing features or developer + documentation, update them accordingly. + +2. Reporting: Provide clear and concise feedback in the pull request regarding + testing outcomes and any discovered issues. + +### Test Merging Into Production + +1. Additional Testing: If further validation is necessary, a test merge into + production may be scheduled to assess the code in a live environment. It is + imperative that the code functions correctly before proceeding with a test + merge. + +2. Requesting a Test Merge: Authors of pull requests may request a test merge + during the review phase. Additionally, development team members may initiate + a test merge if deemed necessary. + +3. Responsibilities During Test Merge: If your pull request is selected for a + test merge, it is your responsibility to actively manage and update it as + needed for integration into the codebase. This includes promptly addressing + reported bugs and related issues. + +4. Consequences of Non-compliance: Failure to address requested changes during + the test merge process will result in the pull request being reverted from + production and potentially closed. + +Testing plays a vital role in our process. As an open-source project with a +diverse community of contributors, it's essential to safeguard everyone's +contributions from potential issues. Thorough testing not only helps maintain +the quality of our code but also eases the workload for our development team, +who ensure that each pull request meets our agreed-upon standards. By following +these steps, we can ensure a streamlined process when implementing changes. + +Do you need some help with testing your Pull Request? You can ask questions to +the development team on the [Paradise Station Discord][discord]. We also have a +[guide about testing pull requests here!][testing-guide] + +[discord]: https://discord.gg/YJDsXFE +[testing-guide]: testing_guide.md diff --git a/docs/contributing/getting_started.md b/docs/contributing/getting_started.md new file mode 100644 index 000000000000..dae74269ff74 --- /dev/null +++ b/docs/contributing/getting_started.md @@ -0,0 +1,478 @@ +# Getting Started + +Whether you're looking to contribute code, mapping changes, or sprites to +Paradise Station, you will need to set up a development environment. This guide +provides all the necessary steps to do so. + +![](./images/flowchart.png){: style="width:50%"} + +## Development Environment Setup + +This guide will walk you through the basic steps of installing Git, the program +that tracks changes to the Paradise codebase, as well as Visual Studio Code, the +recommended editor for working with Paradise. + +### Setting Up Visual Studio Code + +Visual Studio Code is the recommended editor for working with Paradise and other +SS13 codebases. + +1. Go to VS Code's website: +2. Download the appropriate build for your system and install it. + +### Setting Up Git + +Git is the program that allows you to share your code changes with the Paradise +codebase. Git can be daunting to contributors unfamiliar with source control, +but is an incredibly powerful tool, and there are many resources online to learn +how to use it and understand its concepts. + +To install git: + +1. Go to the [Git][] website and download the installer for your operating system. +2. Run the installer, leaving all the default installation settings. + +For a straightforward beginner's guide to git, see GitHub's [Git Guides][]. + +For more guidance on installing git, see the Git Guide for [Installing Git][]. + +GitHub also provides a graphical environment for working with git called [GitHub +Desktop][]. This is not necessary to contribute to Paradise Station, as Visual +Studio Code also has powerful git integration. + +[Git]: https://git-scm.com/downloads +[Installing Git]: https://github.com/git-guides/install-git +[Git Guides]: https://github.com/git-guides +[GitHub Desktop]: https://github.com/apps/desktop + +#### Additional Help + +For instructional videos on Visual Studio Code's GitHub integration, see . + +For a straightforward beginner's guide to git, see GitHub's [Git Guides][]. + +For more guidance on installing git, see the Git Guide for [Installing Git][]. + +For introductory videos on Git, see: + +- [Introduction to Git with Scott Chacon of GitHub](https://www.youtube.com/watch?v=ZDR433b0HJY) +- [Git From Bits Up](https://www.youtube.com/watch?v=MYP56QJpDr4) +- [Linus Torvalds (inventor of Linux and Git) on Git](https://www.youtube.com/watch?v=4XpnKHJAok8) + +### Registering a GitHub Account + +GitHub is the service where the Paradise codebase is stored. You'll need a +GitHub account to contribute to Paradise. Go to the [GitHub signup page][] and +register with a username and e-mail account. + +[GitHub signup page]: https://github.com/signup + +### Hiding Your Email Address + +Changes to Git repositories include the e-mail address of the person who made +the change. If you don't wish your email address to be associated with your +development on Paradise, you can choose to hide your email when interacting with +repositories on GitHub: + +1. Log into your GitHub account. +2. Go to . +3. Select the _Keep my email addresses private_ checkbox. + +This means that while your e-mail address is associated with your GitHub +account, any changes you make will only be keyed to a generic e-mail address +with your username. + +## Installation for Linux Users + +The code is fully able to run on Linux, however Windows is still the recommended +platform. The libraries we use for external functions (rust-g and MILLA) require +some extra dependencies. + +### Building rust-g for Debian-based Distributions + +1. Download the latest release from +2. Run the following command: + + ```sh + apt-get install libssl-dev:i386 pkg-config:i386 zlib1g-dev:i386 + ``` + +3. After installing these packages, rust-g should be able to build and function + as intended. Build instructions are on the rust-g GitHub. We assume that if + you are hosting on Linux, you know what you are doing. +4. Once you've built rust-g, you can build MILLA similarly. Change into the + `milla/` directory and run: + + ```sh + cargo build --release --target=i686-unknown-linux-gnu + ``` + +## Cloning the Repository + +Cloning the Paradise repository only has to be done once. + +1. Visit the [repository][] and press the _Fork_ button in the upper right corner. + + ![](./images/fork_repository.png) + +[repository]: https://github.com/ParadiseSS13/Paradise + +2. Launch Visual Studio Code. Select the Source Control panel on the sidebar, + and click _Clone Repository_. + + ![](./images/vsc_clone_repository.png) + + If that’s not there, you can press `Ctrl`+`Shift`+`P` to open the command + palette, then type `Git: Clone` and then press `Enter`. + +3. Paste the URL of the repository you created in the last step. It should look + like this: `https://github.com/YOURNAME/Paradise`. Then, select a folder to + keep your local repository. The process of downloading might take a while. + Once it’s downloaded, open the folder in Visual Studio Code. + +## Installing Recommended Visual Studio Code Extensions + +When you first open the Paradise repository in Visual Studio Code, you will also +get a notification to install some recommended extensions. These plugins are +extremely useful for programming with BYOND and should be considered essential. +If you don't see the prompt to install the recommended extensions, they can be +found by searching for `@recommended` in the Extensions panel, or installed from +the list below. + +- [DreamMaker Syntax Highlighting](https://marketplace.visualstudio.com/items?itemName=gbasood.byond-dm-language-support) +- [BYOND Language Support](https://marketplace.visualstudio.com/items?itemName=platymuus.dm-langclient) +- [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) +- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) +- [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) +- [ErrorLens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens) +- [DreamMaker Icon Editor](https://marketplace.visualstudio.com/items?itemName=anturk.dmi-editor) +- [Prettier Code Formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +- [ZipFS](https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs) + +## Adding Paracode as an Upstream Repository + +We need to add the main Paradise repository as a remote now. + +1. Open the command palette (`Ctrl`+`Shift`+`P`), type `Git: Add Remote`, and + press `Enter`. You'll be prompted for the URL of the remote, and then the name + of the remote. + +2. Enter `https://github.com/ParadiseSS13/Paradise` for the URL, and `upstream` + for the name. After you've done that, you’ll have the main Paradise + repository as a remote named `upstream`. This will let you easily send your + pull requests there later. + +## Configuring a Local Database + +While configuring a local database is not required, it is recommended because it +tracks local round IDs, stores local player characters, and allows you to verify +the output of blackbox entries. + +### Initial setup and Installation + +1. Download and install [MariaDB](https://mariadb.com/downloads/mariadb-tx) for + your operating system. The default installation settings should work. You + need TCP enabled and to set a root password. If it offers, do _not_ set it up + to use Windows authentication. If you've ticked Install as a Windows Service + (should be ticked by default), it will run whenever you boot up your + computer, so there's no need to worry about starting it manually. + +2. Open HeidiSQL (comes with MariaDB) and connect it to the database. Click on + *New* to create a new session, check *prompt for credentials* and leave the + rest as default. + +3. Click *Save*, then click open and enter in `root` for the username and the + password you set up during the installation. + +4. Select the database you just created and then select *File -> Load SQL File*, + and open the `paradise_schema.sql` file found in the `SQL/` directory of the + game. + +5. Press the blue "play" icon in the topic bar of icons. If the schema imported + correctly you should have no errors in the message box on the bottom. + +6. Refresh the panel on the left by right clicking it and ensure there's a new + database called `paradise_gamedb` created. + +7. Create a new user account for the server by going to *Tools -> User Manager*. + 'From Host' should be `127.0.0.1`, not `localhost` if hosted locally. + Otherwise, use the IP of the game server. For permissions, do not give it any + global permissions. Instead click *Add Object*, select the database you + created for the server, click *OK*, then give it `SELECT`, `DELETE`, + `INSERT`, and `UPDATE` permissions on that database. + +8. You can click the arrow on the password field to get a randomly generated + password of certain lengths. copy the password before saving as it will be + cleared the moment you hit *Save*. + +9. Open the file `config/config.toml` in your text editor (such as VS Code) + scroll down to the `[database_configuration]` section. You should've copied + the file over from the `config/example` folder beforehand. + +10. Make sure that these settings are changed: + + - `sql_enabled` is set to `true`. + - `sql_version` to the correct version. By starting the server with a + mismatched version here and all the other settings set up, the chat box + will tell you the current version in red text, between the messages for + all the subsystems initializing. Set this to the current version. + - `sql_address` is set to `"127.0.0.1"`. (Replace with the database server's + IP if not hosted locally) + - `sql_port` is set to whatever port was selected during the MariaDB + install, usually `3306`. + - `sql_database` is set to the name of your database, usually + `"paradise_gamedb"`. + - `sql_username` is set to the 'User name' of the user you created above. + - `sql_password` is set to the randomly generated 'Password' of the user you + created above. + +The database is now set up for death logging, population logging, polls, +library, privacy poll, connection logging and player logging. There are two more +features which you should consider. And it's best to do so now, since adopting +them later can be a pain. + +### Database based administration + +Offers a changelog for changes done to admins, which increases +accountability (adding/removing admins, adding/removing permissions, +changing ranks); allows admins with `+PERMISSIONS` to edit other admins' +permissions ingame, meaning they don't need remote desktop access to +edit admins; Allows for custom ranks, with permissions not being tied to +ranks, offering a better ability for the removal or addition of +permissions to certain admins, if they need to be punished, or need +extra permissions. Enabling this can be done any time, it's just a bit +tedious the first time you do it, if you don't have direct access to +the database. + +To enable database based administration: + +- Open `\config\config.toml` and scroll to the `[admin_configuration]` + section. +- Set `use_database_admins` to `true`. +- Add a database entry for the first administrator (likely yourself). +- Done! Note that anyone set in the `admin_assignments` list will no + longer be counted. +- If your database ever dies, your server will revert to the old admin + system, so it is a good idea to have `admin_assignments` and + `admin_ranks` set up with some admins too, just so that the loss of + the database doesn't completely destroy everything. + +## Working with `config.toml` + +Paradise relies on a configuration file for many key aspects of its operation. +That file is `config\config.toml`, and it contains many setting that are useful +when developing locally. When you first clone the Paradise repository, +that file will not exist; that is because it is ignored by git so that people's +individual configurations do not get uploaded to the master repository. + +In order to modify the settings in it, you must copy the file +`config\example\config.toml` to its parent directory. + +Some helpful uses of `config.toml` follow. + +### Overriding the next map + +If you are testing a specific map, the `override_map` setting under the +`system_configuration` setting can be set to any map datum listed in +[`code/modules/mapping/station_datums.dm`][datums]. If you are testing something +that doesn't require a station map, such as ruins, you can avoid loading a large +map altogether and save yourself some time starting the server by setting +`override_map` to `"/datum/map/test_tiny"`. + +[datums]: https://github.com/ParadiseSS13/Paradise/blob/master/code/modules/mapping/station_datums.dm + +### Enabling or disabling Lavaland and Space levels + +If you do not need to load Lavaland, any of its ruins, or any space ruins, you +can change these settings under `ruin_configuration`: + +- `enable_lavaland`: whether to load the Lavaland z-level. +- `enable_space_ruins`: despite its name, this controls whether ruins are + spawned for both space _and_ Lavaland. +- `minimum_zlevels` and `maximum_zlevels` control the number of space z-levels + generated. If you don't need any, set both to `0`. + +### Enabling or disabling station traits + +You can control whether roundstart station traits roll with the +`add_random_station_traits` setting under the `gamemode_configuration` section. + +## Publishing Changes + +First, let's talk about **branches**. First thing to do is to make a new +branch on your fork. This is important because you should never make +changes to the default(master) branch of your fork. It should remain as +a clean copy of the main Paradise repository. + +**For every PR you make, make a new branch.** This way, each of your +individual projects have their own branch. A commit you make to one +branch will not affect the other branches, so you can work on multiple +projects at once. + +### Branching + +To make a new branch, open up the source control sidebar. Navigate to +the Branches section and open it. You should only have the master +branch. You can create a new branch by going and clicking on the Create +Branch button. + +![](./images/VSCodeBranching.png) + +It will then prompt you at the top of your screen to name your new +branch, then select Create Branch and Switch. For this guide, I'll be +creating a new hat, so I'll name my branch `hat-landia`. If you look at +the bottom left hand corner, you'll see that VS Code has automatically +checked out our +branch: + +![](./images/VSCodeBranchExample.png) + +Remember, **never commit changes to your master branch!** You can work +on any branch as much as you want, as long as you commit the changes to +the proper branch. + +Go wild! Make your code changes! This is a guide on how to contribute, +not what to contribute. So, I won't tell you how to code, make sprites, +or map changes. If you need help, try asking in the `#spriting` or the +`#coding_chat` Discord channels. + +### Changing Code + +You'll find your code to edit in the Explorer sidebar of VS Code; if you need to +find something, the Search sidebar is just below that. + +If you want to use DreamMaker instead, go ahead and edit your files there - once +you save them, VS Code will detect what you’ve done and you’ll be able to follow +the guide from there. + +If you do anything mapping related, it is highly recommended you use +StrongDMM and check out the [Mapping Quickstart](../mapping/quickstart.md). + +Now, save your changes. If we look at the Source Control tab, we'll see +that we have some new changes. Git has found every change you made to +your fork's repo on your computer! Even if you change a single space in +a single line of code, Git will find that change. Just make sure you +save your files. + +### Testing Your Code + +The easiest way to test your changes is to press `F5`. This compiles your code, +runs the server and connects you to it, as well as automatically giving you +admin permissions. It also starts a debugger that will let you examine what went +wrong when a runtime error happens. If you want to avoid the debugger press +`Ctrl` + `F5` instead. + +If `F5` does not automatically start a local server, you might have installed +BYOND on a custom path and VSC did not find it. In this case, try the following: + +1. Press `Ctrl` + `,` to open VSC settings. +2. Type "DreamMaker", select "DreamMaker language client + configuration". +3. Under "DreamMaker: Byond Path", add your path to BYOND (for + example, `D:\Program Files (x86)\BYOND`). +4. Press OK and close the tab. +5. Press `F5` to run the server. + +If that does not work, you can compile it into a dmb file and run it in +Dream Daemon. To do so, select the dmb file, set security to Trusted and +hit GO to run the server. After the server starts you can press the +button above the GO / STOP button (now red) to connect. + +Do note that if you compile the game this way, you need to manually make +yourself an admin. For this, you will need to copy everything from +`/config/example` into `/config`. Then you will need to edit the +`/config/config.toml` file by adding a +`{ckey = "Your Name Here", rank = "Hosting Provider"}` line to the +`admin_assignments` list. + +![](./images/DreamDaemon.png) + +Be sure to always test not only if your changes work, but also if you +didn't actually break something else that might be related. + +### Committing to Your Branch + +Hover over the word **Changes** and press the plus sign to stage all +modified files. It should look like this: + +![](./images/VSCodeStageChanges.png) + +Or, pick each file you want to change individually. Staged files are the +changes you are going to be submitting in commit, and then in your pull +request. Once you've done that, they'll appear in a new tab called +Staged Changes. + +![](./images/VSCodeStagedChanges.png) + +Click on one of the code files you've changed now! You'll see a compare +of the original file versus your new file pop up. Here you can see, line +by line, every change that you made. Red lines are lines you removed or +changed, and green lines are the lines you added or updated. You can +even stage or unstage individual lines, by using the More Actions +`(...)` menu in the top right. + +Now that you've staged your changes, you're ready to make a commit. At +the top of the panel, you'll see the Message section. Type a descriptive +name for you commit, and a description if necessary. Be concise! + +Make sure you're checked out on the new branch you created earlier, and +click the checkmark! This will make your commit and add it to your +branch. It should look like this: + +![](./images/VSCodeCommit.png) + +There you go! You have successfully made a commit to your branch. This +is still 'unpublished', and only on your local computer, as indicated by +the little cloud and arrow icon in the bottom left corner. + +![](./images/VSCodePublishBranch.png) + +Once you have it committed, you'll need to push/publish to your GitHub. +You can do that by pressing the small cloud icon called "publish +branch". + +### Publishing to GitHub + +Go to the [Main repository](https://github.com/ParadiseSS13/Paradise/) +once your branch is published, GitHub should then prompt you to create a +pull request. This should automatically select the branch you just +published and should look something like this. + +![](./images/GithubCreatePR.png) + +If not, you'll need to open a Pull Request manually. You'll need to select +_Compare across forks_, then select the upstream repo and target the master +branch. + +Then, you'll be able to select the title of your PR. The extension will make +your PR with your selected title and a default description. **Before +submitting**, ensure that you have properly created your PR summary and followed +the description template. + +#### Changelogs + +Changelogs should be player focused, meaning they should be understandable and +applicable to your general player. Keep it simple: + + fix: fixed a bug with X when you Y + tweak: buffed X to do Y more damage. + +Avoid coding-lingo heavy changelogs and internal code changes that don't visibly +affect player gameplay. These are all examples of what you shouldn't add: + + tweak: added the NO_DROP flag to X item. + tweak: refactored DNA to be more CPU friendly + +If the only changes you're making are internal, and will not have any effect on +players, you can replace the changelog section with _NPFC_, meaning "No +Player-Facing Changes". + +ShareX is a super useful tool for contributing as it allows you to make GIFs to +display your changes. you can download it [here](https://getsharex.com/). + +If all goes well, your PR should look like this: + +![](./images/ExamplePR.png) + +If you want to add more commits to your PR, all you need to do is just push +those commits to the branch. diff --git a/docs/contributing/images/DreamDaemon.png b/docs/contributing/images/DreamDaemon.png new file mode 100644 index 000000000000..45c79a93cab3 Binary files /dev/null and b/docs/contributing/images/DreamDaemon.png differ diff --git a/docs/contributing/images/ExamplePR.png b/docs/contributing/images/ExamplePR.png new file mode 100644 index 000000000000..7d04111e435c Binary files /dev/null and b/docs/contributing/images/ExamplePR.png differ diff --git a/docs/contributing/images/GithubCreatePR.png b/docs/contributing/images/GithubCreatePR.png new file mode 100644 index 000000000000..1133a4000d1b Binary files /dev/null and b/docs/contributing/images/GithubCreatePR.png differ diff --git a/docs/contributing/images/VSCodeBranchExample.png b/docs/contributing/images/VSCodeBranchExample.png new file mode 100644 index 000000000000..e3892dd4f374 Binary files /dev/null and b/docs/contributing/images/VSCodeBranchExample.png differ diff --git a/docs/contributing/images/VSCodeBranching.png b/docs/contributing/images/VSCodeBranching.png new file mode 100644 index 000000000000..f6fca9285b31 Binary files /dev/null and b/docs/contributing/images/VSCodeBranching.png differ diff --git a/docs/contributing/images/VSCodeCommit.png b/docs/contributing/images/VSCodeCommit.png new file mode 100644 index 000000000000..52c00fdfd908 Binary files /dev/null and b/docs/contributing/images/VSCodeCommit.png differ diff --git a/docs/contributing/images/VSCodePublishBranch.png b/docs/contributing/images/VSCodePublishBranch.png new file mode 100644 index 000000000000..8ceea31cd55c Binary files /dev/null and b/docs/contributing/images/VSCodePublishBranch.png differ diff --git a/docs/contributing/images/VSCodeStageChanges.png b/docs/contributing/images/VSCodeStageChanges.png new file mode 100644 index 000000000000..435416d9e021 Binary files /dev/null and b/docs/contributing/images/VSCodeStageChanges.png differ diff --git a/docs/contributing/images/VSCodeStagedChanges.png b/docs/contributing/images/VSCodeStagedChanges.png new file mode 100644 index 000000000000..caf2ce3218ce Binary files /dev/null and b/docs/contributing/images/VSCodeStagedChanges.png differ diff --git a/docs/contributing/images/flowchart.png b/docs/contributing/images/flowchart.png new file mode 100644 index 000000000000..4fb4ebffffab Binary files /dev/null and b/docs/contributing/images/flowchart.png differ diff --git a/docs/contributing/images/fork_repository.png b/docs/contributing/images/fork_repository.png new file mode 100644 index 000000000000..a4381c823e3a Binary files /dev/null and b/docs/contributing/images/fork_repository.png differ diff --git a/docs/contributing/images/reviewer_add_pr_suggestion.png b/docs/contributing/images/reviewer_add_pr_suggestion.png new file mode 100644 index 000000000000..7b3d0cc87f0c Binary files /dev/null and b/docs/contributing/images/reviewer_add_pr_suggestion.png differ diff --git a/docs/contributing/images/reviewer_pr_comment.png b/docs/contributing/images/reviewer_pr_comment.png new file mode 100644 index 000000000000..7ea843be4629 Binary files /dev/null and b/docs/contributing/images/reviewer_pr_comment.png differ diff --git a/docs/contributing/images/reviewer_pr_conversation.png b/docs/contributing/images/reviewer_pr_conversation.png new file mode 100644 index 000000000000..9c095126faa8 Binary files /dev/null and b/docs/contributing/images/reviewer_pr_conversation.png differ diff --git a/docs/contributing/images/reviewer_pr_suggestion_text.png b/docs/contributing/images/reviewer_pr_suggestion_text.png new file mode 100644 index 000000000000..7b9783a9677a Binary files /dev/null and b/docs/contributing/images/reviewer_pr_suggestion_text.png differ diff --git a/docs/contributing/images/reviewer_pr_suggestions.png b/docs/contributing/images/reviewer_pr_suggestions.png new file mode 100644 index 000000000000..30d818335f97 Binary files /dev/null and b/docs/contributing/images/reviewer_pr_suggestions.png differ diff --git a/docs/contributing/images/vsc_clone_repository.png b/docs/contributing/images/vsc_clone_repository.png new file mode 100644 index 000000000000..0f48d8004d28 Binary files /dev/null and b/docs/contributing/images/vsc_clone_repository.png differ diff --git a/docs/contributing/quality_prs.md b/docs/contributing/quality_prs.md new file mode 100644 index 000000000000..fa2c36e5c4fa --- /dev/null +++ b/docs/contributing/quality_prs.md @@ -0,0 +1,272 @@ +# A Guide to Good Quality Pull Requests + +by *Sirryan* + +Hello community members! This guide will help you improve your contributions to +our awesome codebase. To do this, I hope to help you understand how to make PRs +that are atomic, easy to review, and well-documented. + +## Introduction + +Like most contributors on Paradise, my interest in contributing to our codebase +sparked from my love for the game and a desire to improve it. Learning how to +even run a local server and set up git is the first hurdle, followed by learning +DM, and eventually crawling your way to making your first change to a local +branch. At this point one's love for contributing can start to bloom because +you're capable of molding the game into something more pleasing for yourself. + +However, your newly-developed skills and motivation must also be accompanied +with certain responsibilities and adherence to our community guidelines. Instead +of editing code in a bubble, you are now submitting changes to an actual game, +played by hundreds of players, and supported by 100s of community members. You +must not only sell your change to our development team but also make sure that +we can understand what's being changed in the first place and that your change +is beneficial for the server. + +## Making "Good" Pull Requests + +Pulling back from the coding world for a moment, I want to talk about another +community based platform, Wikipedia. One of my favorite things about Wikipedia +is the thorough, battle-tested article quality review system they have. The +crown jewel of this rating system is called a "good article" and it embodies +everything desirable you could ever want in a wikipedia article whether you're a +writer or a reader. While "good articles" are a restricted rating (your topic +has to be important enough), there are lower ratings any article can attain +which sets the best standards for all articles! I feel as though Github Pull +Requests can be similarly scrutinized. + +Much like a "Good Article," on our GitHub a "Good PR" has the following +characteristics: + +- **Your PR is limited in scope.** The PR only has one intended purpose or + change. the PR _only_ changes code that is needed for the purpose of that + change. +- **Your PR is designed to be reviewed.** You leverage code comments and design + logic in a way that a relatively experienced reviewer can understand your + change quickly. +- **Your PR is properly documented.** The entire PR template is filled out, all + changes are documented, and you provide ample justification for your changes. +- **Your PR is tested.** You loaded your changes on a local test server and + systematically checked all changes. This testing is not only documented but + very thorough. + +All of these things will ensure ease of review and expedient flow of your Pull +Request through our GitHub pipeline. To best understand how to reach a level of +quality such as a "Good PR," I'm going to break down each of those points in +detail. + +## Limit your Pull Request's Scope + +One common issue with PRs is something called **"scope creep"** where the scope +of your pull request--the full window of files/lines you are changing--expands +beyond the original intended changes of the PR. Expanding your pull-request +extends voting, review, and testing in such a way that your PR is at a higher +risk of becoming stale, getting conflicted, and potentially being closed. So it +is in your best interest to constrain the size of your PR as much as possible. + +But where's a good place to start? Well, when you're writing your intended +change, try not get side-tracked adding that miscellaneous feature, tweaking +that related system, and/or thinking about altering a few `to_chat`'s here and +there along the way. You might feel like you're improving the game (you probably +are) but this distracts from the more important aspect of your Pull Request, +which is the original intent of your PR. This is a great time to write those +off-topic ideas/changes down somewhere else where you can maybe think about +picking them up later in another PR. + +By limiting your pull request to your originally planned (singular) change you +are keeping your Pull Request **atomic.** Specifically you should: + +- **Focus on One Issue:** If you're fixing a bug, limit your PR to that bug fix. + If you're adding a feature, don't mix it with unrelated refactoring. Pick one + thing and do it really well! +- **Keep it Small and Manageable:** Smaller PRs are easier to review and less + likely to introduce new bugs. It's a lot easier to deconflict and debug 10 PRs + that each change 5 files than to deconflict and debug 1 PR that changes 50. +- **Incremental Changes:** Break down large changes into smaller, logical parts. + Submit these parts as separate PRs. If your feature requires a refactor of a + system or several bug fixes to an object, do those first separately in a + different PR before expanding content. + +While I'm mostly writing this section to make my life easier when I inevitably +review your Pull Request down the road… keeping your PRs small and atomic go a +long way to preserving your time & energy. + +## Make Your Code Easier to Review + +**Fact:** A well-structured & documented PR is easier and faster to review. + +When someone opens a Pull Request, all of the files changed is brand new code to +me for the most part. Especially if you are refactoring or adding new content. +So I need a bit of help or I'm gonna be banging my head against my monitor for a +while. After 30 minutes of confused reading I'm going to abandon my review and +tell you to document your code better before I start again. + +To help you visualize, I would like to take you on a minor code adventure. +Either through GitHub search or your local code editor find the definition for +`/datum/controller/subsystem`, it should be in `subsystem.dm`. Take a peak +around that file, you'll end up seeing a whole lot of code comments. Pick a +random variable or proc and try to explain what it does (rubber ducky debugging +style) to an imaginary friend. Now imagine trying to do that without any of the +comment documentation in the file. You likely can't, not without digging for a +long while and following all of the code logic yourself. Do you even know where +to start? + +I'm not going to write an essay on this section since it really boils down to a +lot of best practices you will learn along the way, so here's some bullets to +brush over: + +**Code Comment:** + +- **Function Headers:** Add comments at the beginning of each function to + explain its purpose, input parameters, and return values. +- **Complex Logic:** Place comments above or alongside complex or non-obvious + code sections to clarify the logic and intent. +- **Avoid Obvious Comments:** Do not comment on code that is self-explanatory, + as it can clutter the codebase. + +**Naming Conventions:** + +- **Descriptive Names:** Use clear and descriptive names for functions and + variables that convey their purpose and use. Name it how it is please! +- **Consistency:** Follow the Paradises naming conventions (snake_case for most + things). +- **Avoid Abbreviations:** Use full words instead of abbreviations unless they + are widely understood and standard within the codebase. +- **Action-Oriented:** Name functions based on what they do (e.g., + calculate_score(), visible_message(), update_appearance()). +- **Meaningful and Contextual:** Choose names that reflect the variable's role + or content (e.g., telecrystal_count, account_pin, summoner). + +By adhering to these practices, you help ensure that your code is understandable +and maintainable, making it easier for reviewers and other contributors to work +with. + +## Fill Out the PR Template + +When creating a pull request, it's crucial to provide clear, detailed +information. Your title and description are the first things anyone sees and are +essential for communicating the intent and scope of your changes to the Design, +Balance, and Review team. + +**Be #26094 and Not #7003** + +This means your PR should be sufficiently presented, like #26094, which is +detailed and precise, rather than vague and insufficient, like #7003. + +**Use Proper PR Titles and Descriptions** + +- **Titles:** Use clear and concise titles that summarize the change. Avoid + vague or ambiguous titles. For example, instead of "Fix bug," use "Fix crash + caused by null reference in inventory system." +- **Descriptions:** Provide a detailed description of your changes. Explain what + the PR does, why it's needed, and how it affects the game. This should be a + comprehensive overview that leaves little to interpretation. + +**Communicating and Visualizing Your Changes** + +When modifying game features, include: + +- **Proper Descriptions:** Clearly describe the changes made to features. + Explain how these changes alter the game's functionality or balance. +- **Screenshots and Videos:** Provide visual evidence of changes, such as + screenshots or videos. This is especially important for UI changes, sprite + updates, or any feature that affects the visual aspect of the game. Include + before-and-after images if applicable. +- **Proof of Functionality:** Demonstrate that the feature works as intended in + the game. This could include video clips of gameplay or detailed descriptions + of test cases and results. + +**Provide Justifications for Changes** + +It's important to justify why your changes are beneficial: + +- **Rationale:** Explain why the change is necessary. Avoid stating personal + opinions without support. Instead, provide a well-reasoned explanation of the + issue being addressed or the improvement being made. +- **Community Consensus:** Mention if there has been discussion among credible + community members. Link to relevant forum threads, Discord conversations, or + other community discussions that show a consensus or strong support for the + change. +- **Data and Evidence:** If applicable, provide data or consistent anecdotal + evidence to support the need for the change. This could include bug reports, + player feedback, or metrics showing a problem with the current state of the + game. + +You must ensure that your PR provides all necessary information for a thorough +review. This not only helps maintain the quality and balance of the game but +also speeds up the review process by reducing back-and-forth questions and +clarifications. Remember, a well-prepared PR reflects well on you as a +contributor and helps maintain a high standard for the codebase. + +## Test Your Code + +Testing your code is a crucial part of our development process, especially when +contributing to a multiplayer game like SS13. While we're working on a +specialized article that will delve deeper into best practices for code testing, +this section will cover the essential aspects and front-facing impacts of +testing, as well as how to effectively communicate your testing efforts in your +pull request. + +**The Basics of Code Testing** + +At a bare minimum, your PR should indicate that you've successfully compiled +your code and tested it on a local test server. This basic step assures +reviewers and other team members that your code runs without immediate issues +and doesn't cause server crashes. However, thorough testing goes beyond just +ensuring the game compiles. + +**Document Your Testing Process** + +When documenting your testing, include detailed steps that you took to verify +your changes. This helps reviewers understand how you tested the functionality +and provides a reference for anyone else looking to verify your work. Consider +the following: + +- **Feature Verification:** Describe how you verified that new features work as + intended. For example, if you added a new item, detail how you tested its + creation, usage, and any unique interactions it might have. +- **Edge Case Testing:** Test and document scenarios where players might use + your feature in uncommon but predictable ways. This helps catch issues that + may not be immediately obvious but could arise in actual gameplay. +- **Performance Considerations:** If your changes could impact game performance, + mention how you tested for this. For example, if you introduced a new loop or + complex logic, describe any stress tests or performance profiling you + conducted. + +**Communicate Testing Results** + +In your PR description, clearly communicate the results of your testing. Did +everything work as expected? Were there any unexpected issues? If you +encountered bugs that you couldn't resolve, note them and explain why they are +there, how they affect the game, and if there are plans to address them later. + +**The Importance of Thorough Testing** + +Thorough testing is vital for maintaining the quality and stability of the game. +It helps prevent embarrassing situations where a new feature is eagerly +anticipated by players, only to be released in a broken or incomplete state. +Reverting a PR or dealing with numerous bug reports due to avoidable issues can +be frustrating for both the development team and players. + +**Additional Resources** + +For a more comprehensive guide on testing your PR, refer to the [Guide to +Testing](../coding/testing_guide.md). This resource will provide detailed +instructions and best practices for ensuring your changes are robust and +reliable. Testing your code is not just a technical necessity; it's a +professional courtesy to your fellow developers and the player community. By +taking the time to thoroughly test and document your changes, you contribute to +a more enjoyable and stable game experience for everyone. + +## Conclusion + +As someone who's been developing at Paradise for several years and has briefly +served as a head of staff, the quality of our community is of high importance to +me. This article is a call to our contributing community to elevate their pull +requests to a quality they can truly be proud of. While Paradise may not always +lead in content expansion or player counts, it consistently sets the standard +for professionalism and server quality. By adhering to the guidelines outlined +in this article, you can continue to help us maintain and enhance our reputation +as a top-tier server, known for its stability, thoughtful design, and vibrant +community. Let's work together to make our GitHub repository a better experience +for everyone who enjoys Paradise. diff --git a/docs/contributing/reviewer.md b/docs/contributing/reviewer.md new file mode 100644 index 000000000000..96dede831cd0 --- /dev/null +++ b/docs/contributing/reviewer.md @@ -0,0 +1,317 @@ +# Reviewer Crash Course + +by *Sirryan* + +Hey everyone, I noticed some people were not sure how to approach reviewing PRs +so I figured I would write up a small guide on PR reviewing and how people like +our Headcoders, commit access, Lewcc, S34N, and I all do our jobs. In addition +to some guidance and pointers on PR reviewing, I will also go over a few code +examples and point out code standard corrections and basic errors that +prospective reviewers can begin to start on. + +## What is code review? + +> Code reviews act as quality assurance of the code base.... *and* can also act +> as a second step in identifying bugs, logic problems, or uncovered edge cases. +> [(source)](https://about.gitlab.com/topics/version-control/what-is-code-review/) + +This is a quote from a gitlab article on the importance of code reviews and code +reviewer teams. It refers to code reviews as a process in which code is +scrutinized (often by more experienced developers); In this process bugs, bad +logic, and unforeseen consequences of changes are uncovered and identified. This +implies that some code will not be high quality, it will have bugs, the logic +used may be illogical, and the actual execution of the code may produce results +not originally intended by the author. By addressing these quality issues in +review, we can eliminate security issues, increase collaboration, discover bugs +earlier, share knowledge, enforce code standards, and ultimately improve our +game. + +Understanding code review first comes from understanding what a pull request is +and the process contributors go through to add their code to the Paradise +Station codebase. When a community member wants to alter the game in some +format, be it a feature, backend processes, or artistic style, they must modify +our game code in some fashion. They will often do this through services such as +visual studio code, GitHub desktop, gitkraken, tortoisegit, etc. Eventually they +will be ready to request that our repository owners merge their new changes into +the codebase. This is a pull request. + +This is the point where code review comes in. The author's code is now publicly +visible and therefor available for anyone with an account to review. If you have +closely followed any PR that changed code in a significant way, you will see +that many people will chip in with their opinion or "comment" on code snippets. + +![image](./images/reviewer_pr_comment.png) + +This is the most basic form of review. Most people may understand "code review" +as developers suggesting changes to the authors proposed changes or providing +critical review of a code structure; Ultimately, review is just a conversation +between two developers. Feedback, questions, and advice are all **valid and +necessary** parts of the code review as much as a code suggestion or comment may +be. In fact, it may be even more important than suggesting the author change a +`src.loc` to a `loc`. Questions such as "What does X do?" or "I know that Y has +done this before, what happens when Z?" ask the author to take a closer look at +their own code and help you understand their intention and goals. Please note: +ITS IMPORTANT TO READ PR DESCRIPTIONS, you should not be reviewing a PR until +you know what it's actually attempting to do. + +**But Sirryan, that's not the *kind* of code review I'm interested in learning +about**. Yes, yes, I know, I'm getting there. While its important to understand +the conversation (and relationship-building) parts of code review, there's also +important technical parts to review that keep our codebase moving. Before +getting into HOW to code review, we will take a look at the two types of +technical code reviews. + +## Comments +Basic comments are when a reviewer leaves a message, question, or directive for +the PR author at a certain code line or chunk. For example, SteelSlayer has left +a comment on Line 12 of `objective.dm` inquiring about a certain variable. The +focus of this conversation here is on this one comment and there is room for the +author (and possibly other reviewers) to enter the discussion. Commenting on +specific places on code helps keep the conversation focused and allows minor +issues to be addressed quickly and efficiently. + +![image](./images/reviewer_pr_conversation.png) + +## Suggestions +Suggestions are when a reviewer suggests/requests a change to a certain line or +chunk of code. This leaves less agency for the PR author (especially when +suggested by a development team member or experienced reviewer) but allows for +the issue to be cleared up much more quickly and with only the click of a couple +buttons. In this case, I have physically wrote out the code I would like to +change this line to, in this case I want `if(contents.len>=max_n_of_items)` +changed to `if(length(contents) >= max_n_of_items)`. These types of reviews are +most critical for enforcing code standards and making 1-5 line corrections. + +![image](./images/reviewer_pr_suggestions.png) + +## Leaving PR Reviews +The way you leave any form of comment or suggestion directly on a line or chunk +of code is under the "Files Changed" tab of the pull request. All you need to do +now is scroll down to a line of code that you want to comment on and hover over +it, a blue plus button will appear and you can click on it to open a comment +menu. + +![image](./images/reviewer_add_pr_suggestion.png) + +You can leave feedback, ask questions, whatever. If you want to suggest changes +to the code, you will need to click the code review icon on the text tool bar. +This will automatically populate a suggestion template in your comment, +everything inside the triple tildes will be part of the suggested code. The code +inside initially will be identical to the PR authors code but you can do +whatever to it, including changes values, editing indentation, or even +adding/removing entire lines. + +![image](./images/reviewer_pr_suggestion_text.png) + +Finally, once you wish to submit this comment or suggestion you have two +options. You can just submit it as is, or you can add it to a batched review +(referred to as "start a review"). If you are doing many +comments/suggestion(2+), you should batch your reviews. If you do batch your +reviews, you can submit them together in the top right of the files changes tab +once done. + +## What can I start reviewing? +So you know what reviewing is, you know how to review, and you're ready to +review.... but what do you review? Knowledge of code and willingness to +understand our currently implemented systems is critically important to being +able to review pull requests. However, there are a few "code standards" you can +look out for on PR's to get familiarized with the code review process and get a +few reviews under your belt. + +### Problematic Code Examples +Lets say a contributor has opened a pull request adding a brand-new item to the +game. This item has a few special functions and procs that you need to look +over. I will go through each part of this code that I would leave comments or +suggestions on, for the most part this covers all the basic things you will look +out for as a beginner code reviewer. + +```dm +/obj/item/omega + name = "Omega Item" + desc = "This object is super duper cool!!!" + icon = 'icons/obj/food/food.dmi' + icon_state = "omega" + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + //determines whether the person using this is "cool" or not + var/is_cool = null + var/list/announce_verbs = list(cool,epic,sick,spectacular) + +/obj/item/omega/attack_self(mob/user) + if(user) + if(is_user_cool(user) && istype(usr, /mob/living/carbon/human)) + to_chat(usr, "[usr] is very [pick(announce_verbs)]") + is_cool = TRUE + return + to_chat(usr, "[usr] is not very [pick(announce_verbs)]") + is_cool = FALSE + return + +/obj/item/omega/proc/is_user_cool(mob/user) + if(istype(usr, /mob/living/carbon/human)) + return 1 + return 0 +``` + +First `var/is_cool = null` needs to be corrected to `var/is_cool`. Any time you +establish a variable in its definition, it will initialize as 'null' if you do +not provide a default value. Therefor, we don't need to assign it a default +value of null because it's redundant. + +Second I immediately see a spacing problem with the list variable, there's not +spacing between comma separators `list(cool,epic,sick,spectacular)`, you should +correct this to be `list(cool, epic, sick, spectacular)` + +```dm +/obj/item/omega + name = "Omega Item" + desc = "This object is super duper cool!!!" + icon = 'icons/obj/food/food.dmi' + icon_state = "omega" + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + //determines whether the person using this is "cool" or not + var/is_cool + var/list/announce_verbs = list(cool, epic, sick, spectacular) +``` + +Lets move onto the `attack_self` proc now. + +We can see that it takes one parameter: `mob/user`. The first thing that +immediately catches my eye is the liberal use of `usr`. Now, `usr` is a native +variable of dm that refers to the mob or user that initiated the calling of the +proc, however this doesn't always mean that `usr` is the same thing as +`mob/user` and may even change depending on the context in which the proc is +called. However, we know that `mob/user` will always be the user we need here +(unless someone screwed up elsewhere) and will use that instead of `usr. + +Next, our first `to_chat` call is miss a span class definition. We will add that +in `` and close it with `` within the parentheses. +With that in mind, we also notice that the second `to_chat` forgot to close +their span, so we will do that as well. + +```dm +/obj/item/omega/attack_self(mob/user) + if(user) + if(is_user_cool(user) && istype(user, /mob/living/carbon/human)) + to_chat(user, "[user] is very [pick(announce_verbs)]") + is_cool = TRUE + return + to_chat(user, "[user] is not very [pick(announce_verbs)]") + is_cool = FALSE + return +``` + +Now lets take a look at the logic here. What does `if(is_user_cool(user) && +istype(user, /mob/living/carbon/human))` do? It's performing an istype check, +and also checking for the return of `is_user_cool()` + +```dm +/obj/item/omega/proc/is_user_cool(mob/user) + if(istype(usr, /mob/living/carbon/human)) + return 1 + return 0 +``` + +More issues! First and foremost, we know that this proc is supposed to return +`TRUE` or `FALSE` so we want to make sure to correct those `0`s and `1`s to +their respective `FALSE` and `TRUE` defines. We should also nip that `usr`. One +final thing with this proc in particular when using istype's, we sometimes +already have defines for specific types. In this case, we already have an +`ishuman()` define + +```dm +#define ishuman(A) (istype(A, /mob/living/carbon/human)) +``` + +Lets make those corrections + +```dm +/obj/item/omega/proc/is_user_cool(mob/user) + if(ishuman(user)) + return TRUE + return FALSE +``` + +Now lets looks at the big picture, you may have noticed that we perform the same +`istype` check **twice**. The author appears to have accidentally added redundant +code in their if check. Let's fix that for them: + +```dm +if(is_user_cool(user)) +``` + +Lets put all of our suggested changes together! + +```dm +/obj/item/omega + name = "Omega Item" + desc = "This object is super duper cool!!!" + icon = 'icons/obj/food/food.dmi' + icon_state = "omega" + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + //determines whether the person using this is "cool" or not + var/is_cool + var/list/announce_verbs = list(cool, epic, sick, spectacular) + +/obj/item/omega/attack_self(mob/user) + if(user) + if(is_user_cool(user)) + to_chat(user, "[user] is very [pick(announce_verbs)]") + is_cool = TRUE + return + to_chat(user, "[user] is not very [pick(announce_verbs)]") + is_cool = FALSE + return + +/obj/item/omega/proc/is_user_cool(mob/user) + if(ishuman(user)) + return TRUE + return FALSE +``` + +That code looks a lot better, it's not perfect and it may not be "balanced" but +the code is much cleaner and even less prone to failure. There is still 7 minor +issues or possibly problematic code in this pull request that can be fixed (and +one that will cause compile errors or runtimes!); **I invite you to look for +them and share in the replies to this post what they are and how you would +suggest to fix them as a PR reviewer.** + +## The Art of Code + +> ... I like it because I could make the computer do what I wanted and every +> time I did that, I got this little thrill and this rush and throughout *my +> entire career*. That thrill for me has never gone away [[The Art of Code - Dylan Beattie](https://www.youtube.com/watch?v=6avJHaC3C2U)] + +This segment might be a bit corny but I figured it would be important to include +because I felt like it was an important aspect of reviewing that I've always had +to keep in mind (and constantly struggle with). Code is not just code, it's the +work or "artwork" of someone else who may have spent a significant amount of +time writing it. Like any language, dreammaker is not particularly easy to learn +for the average player, many of us didn't learn a coding language before trying +our hand at contributing to the codebase. + +## Not All Code Is Equal + +What I mean by this is not that there is some amount of "worth" conferred +between two different works of code; For you and me, we likely have differing +levels of skill, so you writing code for a new custom mob may be extremely easy +but for me, it may be extremely difficult and one of the more difficult tasks I +have attempted. At the same time, I may be much better at working with complex +datums whereas you don't know where to start to build those into a larger +system. We all enter into our dev community with differing levels of skills and +talents, reviewers need to recognize this. + +This appreciation of diverse abilities is important in the sense that we should +not impose judgment on other people's code immediately. Do your absolute best to +avoid coming across as hostile, demanding, or rude in your review comments. +Positive and constructive feedback is important. Most of the time, bad code is +just a consequence of the coder not knowing how to properly do something and +should be treated as a learning experience. + +## Conclusion + +**This is the end of the guide**, I do hope to write an intermediate guide in +the future but I hope this serves well as an entry into reviewing. As always, +questions are always welcome (and criticism/recommendations to this guide). diff --git a/docs/css/atom-one-dark.css b/docs/css/atom-one-dark.css new file mode 100644 index 000000000000..3c51ce688a86 --- /dev/null +++ b/docs/css/atom-one-dark.css @@ -0,0 +1,90 @@ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +code.hljs { + padding: 3px 5px +} +/* + +Atom One Dark by Daniel Gamage +Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax + +base: #282c34 +mono-1: #abb2bf +mono-2: #818896 +mono-3: #5c6370 +hue-1: #56b6c2 +hue-2: #61aeee +hue-3: #c678dd +hue-4: #98c379 +hue-5: #e06c75 +hue-5-2: #be5046 +hue-6: #d19a66 +hue-6-2: #e6c07b + +*/ +.hljs { + color: #abb2bf; + background: #282c34 +} +.hljs-comment, +.hljs-quote { + color: #5c6370; + font-style: italic +} +.hljs-doctag, +.hljs-keyword, +.hljs-formula { + color: #c678dd +} +.hljs-section, +.hljs-name, +.hljs-selector-tag, +.hljs-deletion, +.hljs-subst { + color: #e06c75 +} +.hljs-literal { + color: #56b6c2 +} +.hljs-string, +.hljs-regexp, +.hljs-addition, +.hljs-attribute, +.hljs-meta .hljs-string { + color: #98c379 +} +.hljs-attr, +.hljs-variable, +.hljs-template-variable, +.hljs-type, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo, +.hljs-number { + color: #d19a66 +} +.hljs-symbol, +.hljs-bullet, +.hljs-link, +.hljs-meta, +.hljs-selector-id, +.hljs-title { + color: #61aeee +} +.hljs-built_in, +.hljs-title.class_, +.hljs-class .hljs-title { + color: #e6c07b +} +.hljs-emphasis { + font-style: italic +} +.hljs-strong { + font-weight: bold +} +.hljs-link { + text-decoration: underline +} \ No newline at end of file diff --git a/docs/css/para.css b/docs/css/para.css new file mode 100644 index 000000000000..f0c5021b48d9 --- /dev/null +++ b/docs/css/para.css @@ -0,0 +1,28 @@ +header { + font-family: ui-rounded, 'Hiragino Maru Gothic ProN', Quicksand, Comfortaa, Manjari, 'Arial Rounded MT', 'Arial Rounded MT Bold', Calibri, source-sans-pro, sans-serif; +} + +:root,[data-md-color-scheme=default] { + + /* Primary color shades */ + --md-primary-fg-color: #861f41; + --md-primary-fg-color--light: #861f4194; + --md-primary-fg-color--dark: #ac325a; + --md-default-fg-color--light: #e6e6e6; + --md-default-fg-color--lighter: #ad2e7352; + --md-default-fg-color: #e6e6e6; + --md-default-bg-color: #22030d; + --md-primary-bg-color--light: #ff266b; + --md-text-link-color: hsla(231, 48%, 48%, 1); + + /* Accent color shades */ + --md-accent-fg-color: rgb(183, 120, 255); + --md-accent-fg-color--transparent: hsla(189, 100%, 37%, 0.1); + --md-accent-bg-color: hsla(0, 0%, 100%, 1); + --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7); + + --md-code-bg-color: #281828; + --md-code-fg-color: #d4d4d4; + + --md-typeset-a-color: #ff558d; + } diff --git a/docs/hooks/contributing_path.py b/docs/hooks/contributing_path.py new file mode 100644 index 000000000000..b4b23e48cefc --- /dev/null +++ b/docs/hooks/contributing_path.py @@ -0,0 +1,36 @@ +"""Upper-case path rewriter. + +While it is not mandatory for the files "CONTRIBUTING" and "CODE_OF_CONDUCT" to +be in uppercase, this promotes visibility within the repo. + +However, if mkdocs has a root file named CONTRIBUTING.md, and a nav section +named "contributing", and the site is built on a case-insensitive filesystem, +these will resolve to the same path, i.e.: + +- CONTRIBUTING.md -> "/contributing/index.html" +- contributing/getting_started.md -> "/contributing/getting_started/index.html" + +When URL links are generated, the original filename is used, so any links to +CONTRIBUTING.md will resolve to "/CONTRIBUTING/index.html". This will break when +published to a webserver, as that directory won't exist. + +This tiny script replaces references to the upper-case file path to a lower-case +file path, to resolve this issue. + +For consistency's sake, we do this for CODE_OF_CONDUCT, despite no worry about +collisions in this case, because the URL looks nicer. +""" + +transforms = { + "CONTRIBUTING": "contributing", + "CODE_OF_CONDUCT": "code_of_conduct", +} + + +def on_page_markdown(markdown, *, page, config, files): + for old, new in transforms.items(): + if page.file.name == old: + page.file.name = new + page.file.url = page.file.url.replace(old, new) + page.file.dest_uri = page.file.dest_uri.replace(old, new) + page.file.abs_dest_path = page.file.abs_dest_path.replace(old, new) diff --git a/docs/images/favicon.png b/docs/images/favicon.png new file mode 100644 index 000000000000..2f923f9b1cac Binary files /dev/null and b/docs/images/favicon.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000000..54b6c1aadc0d --- /dev/null +++ b/docs/index.md @@ -0,0 +1,15 @@ +# Paradise Contributor Documentation + +This site is a comprehensive collection of all documents, guides, and references +necessary to get started making contributions to Paradise Station. + +## Required Reading + +All contributors are expected to read and abide by the Paradise Station [Code of +Conduct](./CODE_OF_CONDUCT.md) and [Contribution Guidelines](./CONTRIBUTING.md). + +## New Contributors + +If you are new to writing code and contributing, start at [Getting Started][]. + +[Getting Started]: ./contributing/getting_started.md diff --git a/docs/javascripts/highlight-dm.js b/docs/javascripts/highlight-dm.js new file mode 100644 index 000000000000..19075ffb21ac --- /dev/null +++ b/docs/javascripts/highlight-dm.js @@ -0,0 +1,113 @@ +// This is a grammar for highlight.js used by mdbook. +// +// - Guide on writing highlighters: https://highlightjs.readthedocs.io/en/latest/language-guide.html +// - possible values for `scope`/`className`: https://highlightjs.readthedocs.io/en/latest/css-classes-reference.html + +/* +Language: Dream Maker +Author: Pieter-Jan Briers +Various bits taken from Javascript, Rust and C++'s file. +Description: Dream Maker language used by the BYOND game engine. +Category: common +*/ + +var BLOCK_COMMENT = hljs.COMMENT('/\\*', '\\*/', {contains: ['self']}); +var KEYWORDS = + 'var proc verb global tmp static const set as ' + + 'new del ' + + 'sleep spawn break continue do else for in step goto if return switch while try catch throw'; +var BUILTINS = + 'usr src world args ' + + 'list datum area turf obj mob atom movable client database exception ' + + 'icon image matrix mutable_appearance savefile sound regex operator'; +var LITERAL = 'null'; +var SUBST = { + className: 'subst', + begin: '\\[', end: '\\]', + keywords: { + built_in: BUILTINS, + literal: LITERAL + }, + contains: [] // defined later +}; +var STRING = { + className: 'string', + begin: '"', end: '"', + contains: [ + hljs.BACKSLASH_ESCAPE, + SUBST + ] +}; +var STRING_MULTILINE = { + className: 'string', + begin: '\\{"', end: '"\\}', + contains: [ + hljs.BACKSLASH_ESCAPE, + SUBST + ] +}; +var FILE_STRING = { + className: 'string', + begin: "'", end: "'" +}; +var NUMBER = { + className: 'number', + variants: [ + { begin: '1\\.\\#IND' }, + { begin: '1\\.\\#INF' }, + { begin: hljs.C_NUMBER_RE }, + ], + relevance: 0 +}; +var CONSTANT = { + className: 'literal', + begin: /\b[A-Z_][A-Z_0-9]*\b/ +}; +var PREPROCESSOR = { + className: 'meta', + begin: /#\s*[a-z]+\b/, end: /$/, + keywords: { + 'meta-keyword': + 'if else elif endif define undef warn error ' + + 'ifdef ifndef include' + }, + contains: [ + { + begin: /\\\n/, relevance: 0 + }, + STRING, + FILE_STRING, + NUMBER, + CONSTANT, + hljs.C_LINE_COMMENT_MODE, + BLOCK_COMMENT, + ] +}; +SUBST.contains = [ + STRING, + FILE_STRING, + NUMBER, + CONSTANT +]; + +hljs.registerLanguage('dm', (hljs) => ({ + aliases: ['byond', 'dreammaker'], + keywords: { + keyword: KEYWORDS, + literal: LITERAL, + built_in: BUILTINS + }, + contains: [ + hljs.C_LINE_COMMENT_MODE, + BLOCK_COMMENT, + PREPROCESSOR, + STRING, + FILE_STRING, + STRING_MULTILINE, + NUMBER, + CONSTANT + ] + })); + +hljs.initHighlightingOnLoad(); + diff --git a/docs/javascripts/highlight.min.js b/docs/javascripts/highlight.min.js new file mode 100644 index 000000000000..9e23dd54ff70 --- /dev/null +++ b/docs/javascripts/highlight.min.js @@ -0,0 +1,618 @@ +/*! + Highlight.js v11.10.0 (git: 366a8bd012) + (c) 2006-2024 Josh Goebel and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(t){ +return t instanceof Map?t.clear=t.delete=t.set=()=>{ +throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{ +const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i) +})),t}class t{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function n(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope +;class o{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)} +closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const r=(e={})=>{const t={children:[]} +;return Object.assign(t,e),t};class a{constructor(){ +this.rootNode=r(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t=r({scope:e}) +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,t){const n=e.root +;t&&(n.scope="language:"+t),this.add(n)}toHTML(){ +return new o(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function l(e){ +return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")} +function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")} +function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break} +s+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0], +"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)} +const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={ +begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t, +contains:[]},n);s.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s +},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({ +__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{ +scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N, +C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number", +begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0}, +NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function L(e,t){ +Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function P(e,t){ +void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={ +relevance:0,contains:[Object.assign(n,{endsParent:!0})] +},e.relevance=0,delete n.beforeMatch +},H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword" +;function $(e,t,n=C){const i=Object.create(null) +;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,$(e[n],t,n))})),i;function s(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){ +return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{ +console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{ +z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0) +},K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={} +;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1]) +;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +K +;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"), +K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +K +;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"), +K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){ +function t(t,n){ +return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) +}class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o +;if(o.isCompiled)return a +;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))), +o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null +;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords), +c=o.keywords.$pattern, +delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)), +a.keywordPatternRe=t(c,!0), +r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/), +o.end&&(a.endRe=t(a.end)), +a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)), +o.illegal&&(a.illegalRe=t(o.illegal)), +o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{ +starts:e.starts?i(e.starts):null +}):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a) +})),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){ +return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{ +constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} +const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{ +const i=Object.create(null),s=Object.create(null),o=[];let r=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function b(e){ +return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s="" +;"object"==typeof t?(i=e, +n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."), +G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o) +;const r=o.result?o.result:E(o.language,o.code,n) +;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){ +const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R) +;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n="" +;for(;t;){n+=R.substring(e,t.index) +;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){ +const[e,i]=o +;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{ +const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0] +;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i +;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{ +if(""===R)return;let e=null;if("string"==typeof N.subLanguage){ +if(!i[N.subLanguage])return void M.addText(R) +;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top +}else e=x(R,N.subLanguage.length?N.subLanguage:null) +;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language) +})():l(),R=""}function u(e,t){ +""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1 +;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue} +const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}} +function h(e,t){ +return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{ +value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e) +;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return f(e.parent,n,i)}function b(e){ +return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){ +const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N +;N.endScope&&N.endScope._wrap?(g(), +u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(), +d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t), +g(),o.excludeEnd&&(R=t));do{ +N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent +}while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length} +let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0 +;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){ +if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=w.rule,t}return 1} +if(w=o,"begin"===o.type)return(e=>{ +const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]] +;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n) +;return i.skip?R+=n:(i.excludeBegin&&(R+=n), +g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o) +;if("illegal"===o.type&&!s){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"")+'"') +;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e} +if("illegal"===o.type&&""===a)return 1 +;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches") +;return R+=a,a.length}const _=O(e) +;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[] +;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{ +if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){ +I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A +;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e) +;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e, +value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A, +context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{ +language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N} +;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{ +const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)} +;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1))) +;s.unshift(n);const o=s.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1 +;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r +;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1]) +;return t||(X(a.replace("{}",n[1])), +X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return +;if(N("before:highlightElement",{el:e,language:n +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML) +;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i) +;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,o.language),e.result={language:o.language,re:o.relevance, +relevance:o.relevance},o.secondBest&&(e.secondBest={ +language:o.secondBest.language,relevance:o.secondBest.relevance +}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0 +}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]} +function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +s[e.toLowerCase()]=t}))}function k(e){const t=O(e) +;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_, +highlightElement:w, +highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"), +G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)}, +initHighlighting:()=>{ +_(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +_(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){ +if(W("Language definition for '{}' could not be registered.".replace("{}",e)), +!r)throw t;W(t),s=l} +s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete i[e] +;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, +listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v, +autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)}, +removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{ +r=!1},n.safeMode=()=>{r=!0},n.versionString="11.10.0",n.regex={concat:h, +lookahead:g,either:f,optional:d,anyNumberOfTimes:u} +;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n +},ne=te({});return ne.newInstance=()=>te({}),ne}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `c` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",s="[a-zA-Z_]\\w*::",i="("+a+"|"+n.optional(s)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},l={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef elifdef elifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(l,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(s)+e.IDENT_RE,relevance:0 +},_=n.optional(s)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","typeof","typeof_unqual","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_BitInt","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal96","_Decimal128","_Decimal64x","_Decimal128x","_Float16","_Float32","_Float64","_Float128","_Float32x","_Float64x","_Float128x","const","static","constexpr","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},g=[c,r,t,e.C_BLOCK_COMMENT_MODE,o,l],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:g.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:g.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+i+"[\\*&\\s]+)+"+_,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:_,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,l,o,r,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,l,o,r] +}]},r,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:l,keywords:u}}}})();hljs.registerLanguage("c",e)})();/*! `diff` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=e.regex;return{name:"Diff", +aliases:["patch"],contains:[{className:"meta",relevance:10, +match:a.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:a.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}}})();hljs.registerLanguage("diff",e)})();/*! `javascript` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;return o=>{const l=o.regex,b=e,d={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="e+"\\s*\\(")), +l.concat("(?!",T.join("|"),")")),b,l.lookahead(/\s*\(/)), +className:"title.function",relevance:0};var T;const C={ +begin:l.concat(/\./,l.lookahead(l.concat(b,/(?![0-9A-Za-z$_(])/))),end:b, +excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},M={ +match:[/get|set/,/\s+/,b,/(?=\()/],className:{1:"keyword",3:"title.function"}, +contains:[{begin:/\(\)/},R] +},B="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+o.UNDERSCORE_IDENT_RE+")\\s*=>",$={ +match:[/const|var|let/,/\s+/,b,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,N,_,f,p,{match:/\$\d+/},A,k,{ +className:"attr",begin:b+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[p,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0 +},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:d.begin, +"on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{ +begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},I,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:b, +className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+b, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})() +;hljs.registerLanguage("javascript",e)})();/*! `json` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],s={ +scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",aliases:["jsonc"], +keywords:{literal:a},contains:[{className:"attr", +begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{match:/[{}[\],:]/, +className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,s,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `markdown` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, +end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, +relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] +}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) +;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g) +})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal", +match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}})() +;hljs.registerLanguage("markdown",e)})();/*! `plaintext` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var t=(()=>{"use strict";return t=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0})})() +;hljs.registerLanguage("plaintext",t)})();/*! `shell` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var s=(()=>{"use strict";return s=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]})})();hljs.registerLanguage("shell",s)})();/*! `sql` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const r=e.regex,t=e.COMMENT("--","$"),n=["true","false","unknown"],a=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],i=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=i,c=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!i.includes(e))),l={ +begin:r.concat(/\b/,r.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} +;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:r,when:t}={})=>{const n=t +;return r=r||[],e.map((e=>e.match(/\|\d+$/)||r.includes(e)?e:n(e)?e+"|0":e)) +})(c,{when:e=>e.length<3}),literal:n,type:a, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:r.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, +keyword:c.concat(s),literal:n,type:a}},{className:"type", +begin:r.either("double precision","large object","with timezone","without timezone") +},l,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", +variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, +contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ +className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, +relevance:0}]}}})();hljs.registerLanguage("sql",e)})();/*! `typescript` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;function o(o){const l=o.regex,d=e,b={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="e+"\\s*\\(")), +l.concat("(?!",C.join("|"),")")),d,l.lookahead(/\s*\(/)), +className:"title.function",relevance:0};var C;const T={ +begin:l.concat(/\./,l.lookahead(l.concat(d,/(?![0-9A-Za-z$_(])/))),end:d, +excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},M={ +match:[/get|set/,/\s+/,d,/(?=\()/],className:{1:"keyword",3:"title.function"}, +contains:[{begin:/\(\)/},R] +},B="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+o.UNDERSCORE_IDENT_RE+")\\s*=>",$={ +match:[/const|var|let/,/\s+/,d,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:x},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,p,N,f,_,h,{match:/\$\d+/},A,x,{ +className:"attr",begin:d+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[h,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0 +},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:b.begin, +"on:begin":b.isTrulyOpeningTag,end:b.end}],subLanguage:"xml",contains:[{ +begin:b.begin,end:b.end,skip:!0,contains:["self"]}]}]},O,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:d, +className:"title.function"})]},{match:/\.\.\./,relevance:0},T,{match:"\\$"+d, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},I,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},k,M,{match:/\$[(.]/}]}}return t=>{ +const s=o(t),r=e,l=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],d={ +begin:[/namespace/,/\s+/,t.IDENT_RE],beginScope:{1:"keyword",3:"title.class"} +},b={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{ +keyword:"interface extends",built_in:l},contains:[s.exports.CLASS_REFERENCE] +},g={$pattern:e, +keyword:n.concat(["type","interface","public","private","protected","implements","declare","abstract","readonly","enum","override","satisfies"]), +literal:a,built_in:i.concat(l),"variable.language":c},u={className:"meta", +begin:"@"+r},m=(e,n,a)=>{const t=e.contains.findIndex((e=>e.label===n)) +;if(-1===t)throw Error("can not find mode to replace");e.contains.splice(t,1,a)} +;Object.assign(s.keywords,g),s.exports.PARAMS_CONTAINS.push(u) +;const E=s.contains.find((e=>"attr"===e.className)) +;return s.exports.PARAMS_CONTAINS.push([s.exports.CLASS_REFERENCE,E]), +s.contains=s.contains.concat([u,d,b]), +m(s,"shebang",t.SHEBANG()),m(s,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),s.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(s,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),s}})() +;hljs.registerLanguage("typescript",e)})();/*! `yaml` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/, +end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]", +contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{ +begin:/\w[\w :()\./-]*:(?=[ \t]|$)/},{begin:/"\w[\w :()\./-]*":(?=[ \t]|$)/},{ +begin:/'\w[\w :()\./-]*':(?=[ \t]|$)/}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", +begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b] +;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:b}}})();hljs.registerLanguage("yaml",e)})(); \ No newline at end of file diff --git a/docs/javascripts/init.js b/docs/javascripts/init.js new file mode 100644 index 000000000000..66ce4773c3d9 --- /dev/null +++ b/docs/javascripts/init.js @@ -0,0 +1 @@ +hljs.highlightAll(); diff --git a/docs/mapping/design.md b/docs/mapping/design.md new file mode 100644 index 000000000000..0eb78cf21fc8 --- /dev/null +++ b/docs/mapping/design.md @@ -0,0 +1,197 @@ +# Mapping Design Guide + +The purpose of this guide is to instruct how to design maps for Paradise. While +there are many resources on the technical aspects of mapping, this guide instead +focuses on considering mapping from a thematic, functional, and balance +perspective. + +## Design Guidelines + +Maps are one of the most visible ways of conveying the world and setting of the +server to players. Maps should work to preserve that setting. Paradise Station +takes place in a 26th century universe where multiple space-faring species work +on stations owned by a pangalactic corporate conglomerate. New maps, ruins, and +remaps should make sense within that world. + +1. ***Use the appropriate decorative elements and turf types.*** Department + flooring should use their associated colors: red for Security, brown for + Cargo, blue for Medbay, and so on. Stations should always use standard walls + and reinforced walls, and not e.g. plastitanium walls. Stations should always + use standard airlocks, and not e.g. syndicate hatches or Centcomm airlocks. + +2. ***Avoid excessive use of decals or floor tile variants.*** Using too many + decals or floor tile variants causes unnecessary visual noise. Only use + decals such as warning tape where it is sensible, e.g. around airlocks that + lead to space. Decal overuse contributes to maptick slowdown. + +2. ***Avoid "Big Room Disease".*** "Big Room Disease" refers to areas on a map + that are unnecessarily large, empty, and/or square. Rooms should be large + enough to handle crew foot traffic and facilitate their use, but no larger. + Furniture should be placed appropriately inside rooms, not just lined along + their walls. Large rooms should rarely be perfectly square or rectangular. + +3. ***Public areas should be interesting.*** "Interesting" is subjective, but + generally, areas such as public hallways should include space for crew + interaction; decorations such as posters, flags, and other decorative + structures; windows that look out onto space, and floor tiles and decals that + delineate the space. + +4. ***Use appropriate hall sizes.*** Primary hallways should be three tiles + wide. Arterial hallways off the primary halls should be two tiles wide. + Intradepartmental corridors should be one or two tiles wide. Exceptions to + this include the Brig, Medbay, and Science, whose main halls can be three + tiles wide due to the amount of foot traffic and number of sub departments + within their space. + +5. ***Properly signpost maintenance areas.*** "Signposting" refers to + environmental factors that make it clear what part of maintenance the player + is in. For example, while "med maints" on Cyberiad is the area around medbay, + it is also distinguishable from its abandoned cryo tube, medical dufflebag, + operating table, and so on. Note however that this signposting does not need + to be directly related to nearby departments: for example, mining maints on + Cyberiad has a small abandoned gambling room, a laundry room, and several + abandoned shower/bathroom areas. This distinguishes it from other maintenance + areas despite not directly referencing mining, as players will eventually + associate these distinct features with that area of maintenance. + +6. ***Ensure continuity of scale.*** The size of rooms should make sense + relative to one another. The chef’s freezer should not be larger than their + kitchen. The dorm’s bathroom should not be larger than the Captain’s office. + The scale of rooms should make sense for their expected occupancy and + purpose. For example, the Heads of Staff Meeting Room should be large enough + to seat all staff comfortably around a table, with extra space for navigating + foot traffic around the table. + + +## Balance Guidelines + +Maps should be an unbiased playing field for players, whether ordinary crew, +silicon, antagonists, or midrounds. Players should not be able to rely on a +specific station layout to gain unique advantages over other players. + +1. ***Maintain consistent loot counts and opportunities.*** The amount of + maintenance loot drops should remain consistent, with a slight scaling factor + based on expected station population. There should be no "treasure troves" or + hoards of loot hidden that can only be found with specific map knowledge. + Department supplies should be consistent across maps. Do not place any + syndicate items/traitor tools on station; always use the provided maintenance + loot spawners to maintain proper statistical likelihood of rare loot spawns. + +2. ***Use appropriate reinforcement.*** Most of the station should be delineated + with ordinary walls and reinforced windows. Only secure areas should use + reinforced walls and grilled spawners, and electrified windows should only be + used in rare cases: department head offices, technical storage, brig, + xenobio, and AI satellite. + +3. ***AI cameras should not have full coverage.*** The AI should not be + permitted to see into every single room. This makes it challenging for + antagonists to accomplish their objectives _in situ_. It is not enough to + have cameras that antagonists can disable, since an AI will notice that the + camera is out, when it is not usually. Areas appropriate for lacking cameras + include Operating Rooms, the Therapist’s office, the Execution Chamber, and + Dormitory bathrooms and shower rooms. Similarly: + +4. ***AI cameras should never be placed in maints.*** This is prohibited + completely. It provides a disproportionately competitive advantage for sec + against antagonists. Currently there is one exception to this, and that is + the cameras immediately outside the solar array maintenance areas on + Cyberiad. These give AI only a vague hint of what is happening nearby, and + very limited visibility into events in maints. + +5. ***Weak points are expected.*** The station is not a battle fortress, and it + is not fun for antagonists to attempt to ingress/egress deliberately + impenetrable areas. For example, Permabrig areas will typically have one or + two tools just out of reach for prisoners to attempt escape. There is a + toolbox in the far end of the gulag island to give gulag prisoners a chance + to escape. The Head of Security’s Office is bordered by outer space on two + station maps. Attempting to break into and out of sensitive areas should be + challenging, but not impossible. + +6. ***Occasionally place vents and scrubbers under furniture.*** Having all + vents and scrubbers prominently visible hinders ventcrawling antagonists. It + is easy for crew to forget to weld vents they cannot see. + +7. ***Ensure security/antag balance for maintenance tunnels.*** This includes + but is not limited to: having a primary path that allows navigability from + all entrances; providing ways for security to flank antagonists and + coordinate ambushes at maintenance entrances; ensuring that the majority of + the primary corridors are 2 tiles wide to allow for serpentine movement and + avoiding projectiles; ensuring that dead ends are rare; and providing places + for antagonists to hide using hidden walls or similarly difficult to find + places. + +8. ***Allow for escape routes and antag break-in routes.*** Departments should + be connected to maintenance through a back or side door. This lets players + escape and allows antags to break in. If this is not possible, departments + should have extra entry and exit points. + +## Functional Guidelines + +Stations are malleable. Players can build, rebuild, decorate, upholster, and +equip the station in many ways. Mappers should take this into account when +designing areas and departments. This goes doubly so for ruins: players will +always find a way to work around the restrictions and intended flow of your +ruin. Attempting to enforce a "correct" way of interacting with a map without +deviation is impossible. + +1. ***Rooms should have specific and clear functions.*** Public rooms should + have a clear purpose. Large maintenance areas should appear to have had a + clear purpose—an abandoned robotics department, for example, or a disused + monkey-fighting ring. The `/area/station` subtypes enumerate most of what + rooms are expected within a station and its departments. Even if a room is + largely meant for player expansion, it should use an appropriate type and + name, such as the Vacant Office. + +2. ***Do not create "perfect" departments.*** The stations are not ideal + workplaces, not state-of-the-art, and not diligently maintained by + Nanotrasen. There should always be a gap between the ideal station and how + the maps are designed. Departments should not come fully featured and + configured, and should require crew interaction to set up and use + effectively. Examples of this include Medbay preparing Operating Rooms, Cargo + arranging the office to make access to the autolathe more convenient, and + Engineering reconfiguring the supermatter’s pipenet. This scarcity is also + critical to crew interactions: the Kitchen should have to rely on botany to + make the full range of recipes, etc. + +3. ***Provide surfaces.*** All jobs require managing many different objects, + items, and pieces of equipment. There should be an adequate number of tables + and racks available for department members to place things down and drop + things off. + +4. ***Place emergency lockers at appropriate intervals.*** Emergency closets and + fire-safety closets should be accessible to crew at regular intervals in + primary hallways, or just off primary hallways in adjacent maintenance + tunnels. + +## Ruin-Specific Guidance + +1. ***Balance the risk/reward ratio of a ruin appropriately.*** If a player + decides that the risk of running a ruin is not worth the reward, they will + stop running it. + +2. ***Not all ruins should provide rare loot.*** "Low-reward" ruins should exist + to balance out random generation, so that every ruin in a round is not a loot + resource. These ruins can be purely decorative, provide a place for + role-play, or provide diegetic/environmental storytelling. + +3. ***Ruins should fit the setting, and have a well signposted purpose.*** If, + for example, your ruin is some kind of abandoned technical/research facility, + it should should have appropriately defined areas: an obvious entrance, a + working area for staff and crew, a testing lab, containment area for living + specimens, some way for staff to have food, restrooms, and living + quarters/showers if they are intended to stay on the facility for extended + periods of time. + +4. ***Avoid ‘magic’ power whenever possible.*** While infinitely regenerating + APCs and SMES units exist, they should be used sparingly. Ruins should be as + realistic as possible and afford players the ability to take advantage of + being powered or unpowered to navigate or exploit the ruin when possible. + +## Shuttle-Specific Guidance + +1. ***Shuttles should have clearly defined secure areas and bridges.*** Secure + areas are for security and prisoner seating. Bridges are for all Command and + dignitaries, and include the emergency shuttle console. Consideration should + be given for hijackers and accessibility to the emergency shuttle console, as + well as the ability of crew to storm the bridge if necessary to prevent a + hijack. diff --git a/docs/mapping/images/atmos_pipes_aligned.png b/docs/mapping/images/atmos_pipes_aligned.png new file mode 100644 index 000000000000..06001f5c82bc Binary files /dev/null and b/docs/mapping/images/atmos_pipes_aligned.png differ diff --git a/docs/mapping/images/atmos_pipes_unaligned.png b/docs/mapping/images/atmos_pipes_unaligned.png new file mode 100644 index 000000000000..3e181b3c7594 Binary files /dev/null and b/docs/mapping/images/atmos_pipes_unaligned.png differ diff --git a/docs/mapping/images/complex_pipes.png b/docs/mapping/images/complex_pipes.png new file mode 100644 index 000000000000..8f43df990ff9 Binary files /dev/null and b/docs/mapping/images/complex_pipes.png differ diff --git a/docs/mapping/quickstart.md b/docs/mapping/quickstart.md new file mode 100644 index 000000000000..130b6f1e341d --- /dev/null +++ b/docs/mapping/quickstart.md @@ -0,0 +1,82 @@ +# Mapping Quickstart + +The purpose of this guide is to provide basic instructions on how to start +mapping for Paradise Station and the tools needed to do so. + +## Tooling + +Once you have set up your [development environment][env], you will need several +other tools to edit maps and publish your changes for Pull Requests. + +### Mapmerge + +If you have a map change published as a PR, and someone else makes a change to +that map which is merged before yours, it is likely that there will be a merge +conflict. Because of the way map files are formatted, using git to resolve these +merge conflicts directly will result in a broken map. + +To deal with this, a separate tool, *Mapmerge*, is integrated into git. Mapmerge +has the ability to look at the changes between two maps, merge them together +correctly, and provide markers on the map where it requires a contributor to +make a manual change. + +To install Mapmerge, run `\tools\hooks\Install.bat`. Further usage of Mapmerge +is documented in the [Guide to Mapmerge](). + +
+ +Unless you know how to use git effectively, install Mapmerge **before** having +to deal with a map merge conflict. + +
+ +[env]: ../contributing/getting_started.md + +### StrongDMM + +[StrongDMM][] is the recommended tool for editing maps by a wide margin. It is +fast, provides easy searching for both objects on maps and objects in the +codebase, an intuitive varediting system, the ability to hide categories of +objects on the map while editing, and more. + +When using StrongDMM, the following options must be enabled. They can be found +under _File -> Preferences_: + + - "Sanitize Variables" must be checked. This removes variables that are + declared on the map, but are the same as their initial value.. (For example: + A standard floor turf that has `dir = 2` declared on the map will have that + variable deleted as it is redundant.) + - "Save Format" must be set to "TGM". + - "Nudge Mode" must be set to "pixel_x/pixel_y". + +[StrongDMM]: https://github.com/SpaiR/StrongDMM/releases + +### UpdatePaths + +_UpdatePaths_ is a utility which make it easier for mappers to share simple +large-scale changes across maps. It does this by allowing mappers to write +scripts which describe those changes, which are then applied to maps. For +example, when migrating pixel-pushed ATMs to their directional helpers, this +script was written: + +``` +/obj/machinery/economy/atm{pixel_x=-32} : /obj/machinery/economy/atm/directional/west +/obj/machinery/economy/atm{pixel_x=32} : /obj/machinery/economy/atm/directional/east +/obj/machinery/economy/atm{pixel_y=-32} : /obj/machinery/economy/atm/directional/south +/obj/machinery/economy/atm{pixel_y=32} : /obj/machinery/economy/atm/directional/north +``` + +This takes each object found on a map with the specified `pixel_x`/`y` varedits, +and replaces them with the object on the right side of the line. + +More information on UpdatePaths and how to use it is available in the +[UpdatePaths documentation][upd]. + +[upd]: https://github.com/ParadiseSS13/Paradise/blob/master/tools/UpdatePaths/readme.md + +## Mapping Tutorial + +Until a more specific guide is written for Paradise Station, /tg/station's +[Guide to Mapping](https://hackmd.io/@tgstation/SyVma0dS5#san7890s-A-Z-Guide-to-Mapping) +written by san7890 is a recommended resource for use SDMM, test mapping changes, +and reconcile map merge conflicts. diff --git a/docs/mapping/requirements.md b/docs/mapping/requirements.md new file mode 100644 index 000000000000..c0a01c5f406f --- /dev/null +++ b/docs/mapping/requirements.md @@ -0,0 +1,196 @@ +# Mapping Requirements + +In order for mapping changes to comply with our existing codebase, conventions, +and in-game systems, there are several guidelines that must be followed. + +## Technical Standards + +### Atmospherics and Cables + +1. Unless absolutely necessary, do not run atmospherics pipes or disposals pipes + under wall turfs. **NEVER** run cables under wall turfs. + +2. Every room should contain at least one air vent and scrubber. Use the + following "on" subtype of vents and scrubbers as opposed to varediting: + `/obj/machinery/atmospherics/unary/vent_scrubber/on` and + `/obj/machinery/atmospherics/unary/vent_pump/on`. + +3. Run air pipes together where possible. The first example below is to be + avoided, the second is optimal: + + ![](./images/atmos_pipes_unaligned.png) ![](./images/atmos_pipes_aligned.png) + + Pipe layouts should be logical and predictable, easy to understand at a + glance. Always avoid complex layouts like in this example: + + ![](./images/complex_pipes.png) + +4. External areas, or areas where depressurisation is expected and normal, + should use airless turf variants to prevent additional atmospherics load. + +5. Tiny fans (`/obj/structure/fans/tiny`) can be used to block airflow into + problematic areas, but are not a substitute for proper door and firelock + combinations. They are useful under blast doors that lead to space when + opened. + +### Wall Mounts + +1. Every station area (`/area/station` subtypes) should contain only one APC and + air alarm. + +2. Critical infrastructure rooms (such as the engine, arrivals, and medbay + areas) should be given an APC with a larger power cell. Use the + `/obj/machinery/power/apc/important` and `/obj/machinery/power/apc/critical` + mapping helpers for this purpose. + +3. Every room should contain at least one fire alarm. Fire alarms should not be + placed next to expected heat sources. + +4. Every room should contain at least one station intercom. Intercoms should be + set to frequency `145.9`, and be speaker ON Microphone OFF. This is so radio + signals can reach people even without headsets on. Larger rooms will require + more than one at a time. + +5. Every room should have at least one security camera with the caveats listed + in the [Design Guide](design.md). Larger rooms may require more than one + security camera. All security cameras should have a descriptive name that + makes it easy to find on a camera console. A good example would be the + template \[Department name\] - \[Area\], so Brig - Cell 1, or Medbay - + Treatment Center. Consistency is key to good camera naming. + +6. Every room should have at least one light switch. When possible, light + switches should be placed in such a position that a player can activate them + while standing on the same tile as the room's airlock. Players should not + have to wander through a dark room to find the light switch. + +7. Head of Staff offices should contain a requests console, using the + `/obj/machinery/requests_console/directional` helpers. Console department + names and types should not be varedited. + +8. Use lights sparingly. They draw a significant amount of power. + +### Windows, Walls, and Floors + +1. Electrochromic windows (`/obj/structure/window/reinforced/polarized`) and + doors/windoors (using the `/obj/effect/mapping_helpers/airlock/polarized` + helper) are preferred over shutters as the method of restricting view to a + room through windows. Shutters are sill appropriate in industrial/hazardous + areas of the station (engine rooms, HoP line, science test chamber, etc.). + Electrochromic window/windoor/door sets require a unique ID var, and a + window tint button (`/obj/machinery/button/windowtint`) with a matching ID + var. The default `range` of the button is 7 tiles but can be amended with a + varedit. + +2. Windows to secure areas or external areas should be reinforced. Windows in + engine areas should be reinforced plasma glass. Windows in high security + areas, such as the brig, bridge, and head of staff offices, should be + electrified by placing a wire node under the window. + +3. Engine areas, or areas with a high probability of receiving explosions, + should use reinforced flooring if appropriate. + +### Airlocks, Windoors, and Firelocks + +1. Firelocks should be used at area boundaries over doors and windoors, but not + windows. Firelocks can also be used to break up hallways at reasonable + intervals. Double firelocks are not permitted. Maintenance access doors + should never have firelocks placed over them. + +2. Door and windoor access must be correctly set by the + `/obj/effect/mapping_helpers/airlock/access` and + `/obj/effect/mapping_helpers/airlock/windoor/access` [helpers][], + respectively. Pay attention to the `any` and `all` subtypes; the `any` + subtype allows someone with any of the accesses on the airlock to use it, + and the `all` subtypes requires the user to have all of the access on the + airlock to use it. + + For example, on the Cerebron (Metastation), miners must walk through the + Cargo Bay to access the Mining Dock. They do not have Cargo Bay access, + rather the Cargo Bay airlocks have two access helpers on them: + + - `/obj/effect/mapping_helpers/airlock/access/any/supply/cargo_bay` + - `/obj/effect/mapping_helpers/airlock/access/any/supply/mining` + + This allows both cargo technicians and miners to use those airlocks. + + Old doors that use var edited access should be updated to use the correct + access helper, and the var edit on the door should be cleaned. + +### Other + +1. Edits in mapping tools should almost always be possible to replicate + in-game. For this reason, avoid stacking multiple structures on the same + tile (e.g. placing a light and an APC on the same wall). + +2. When adding new shuttles, or remapping departures areas, contributors must + ensure that all existing and new shuttles continue to fit and dock to the + correct airlocks as expected. Any time docking ports are edited, the author + needs to confirm the `width`, `height`, and `dwidth` variables between the + two permanent ports and mobile port are compatible. + +[helpers]: https://github.com/ParadiseSS13/Paradise/blob/master/code/modules/mapping/access_helpers.dm + +### Varedits + +*Varediting*, or variable editing, is the term for modifying a variable of an +object on the map, instead of in code. There are many legitimate reasons to do +so. For example, since nearly all floor tiles on the station are the same +object, their `icon_state` and `dir` variables need to be edited to modify their +appearance. + +However, there are also cases when varediting is not appropriate. In general, +when modifying the behavior of an object, creating a subtype in code is almost +always the better option. For example, let's say you have an `/obj/helmet` with +a variable, `strength`, which defines how much damage it can take. The default +is 10. You want to create a stronger helmet, so you add one into a map, and +varedit its `strength` to be 20. This may work for now, but what if the strength +of a helmet no longer is based off that variable? Your helmet will no longer +work as expected. If you instead made an `/obj/helmet/strong`, and made the +variable change there, then if the implementation for `/obj/helmet` changes, +your object will benefit from those changes. + +Another example of inappropriate varediting is doing it to an object with many +instances on a map, or multiple instances across maps. If you need to change the +variable, you will then have to find every instance of it across all of the +maps, and change them all. + +Areas should **never** be varedited on a map. All areas of a single type, +altered instance or not, are considered the same area within the code, and +editing their variables on a map can lead to issues with powernets and event +subsystems which are difficult to debug. + +Subtypes only intended to be used on ruins should be contained within an .dm +file with a name corresponding to that map within `code\modules\ruins`. This is +so in the event that the map is removed, that subtype will be removed at the +same time as well to minimize leftover/unused data within the repo. + +When not using StrongDMM (which handles the following automatically) please +attempt to clean out any dirty variables that may be contained within items you +alter through varediting. For example changing the `pixel_x` variable from 23 to +0 will leave a dirty record in the map's code of `pixel_x = 0`. + +Unless they require custom placement, when placing the following items use the +relevant directional mapper, as it has predefined pixel offsets and directions +that are standardised: APC, Air alarm, Fire alarm, station intercom, newscaster, +extinguisher cabinet, light switches. + +## Mapper Contribution Guidelines + +These guidelines apply to **all** mapping contributors. + +For mapping PRs, we do not accept 'change for the sake of change' remaps, unless +you have very good reasoning to do so. Maintainers reserve the right to close +your PR if we disagree with your reasoning. Large remaps, such as those to a +department, must be justified with clear, specific reasons. + +Before committing a map change, you **MUST** run Mapmerge to normalise your +changes. You can do this manually before every commit with +`tools\mapmerge2\Run Before Committing.bat` or by letting the +[git hooks](./quickstart.md#mapmerge) do it for you. Failure to run Mapmerge on +a map after editing greatly increases the risk of the map's key dictionary +becoming corrupted by future edits after running map merge. Resolving the +corruption issue involves rebuilding the map's key dictionary. + +If you are making non-minor edits to an area or room, (non-minor being anything +more than moving a few objects or fixing small bugs) then you should ensure the +entire area/room is updated to meet these standards. diff --git a/.github/AUTODOC_GUIDE.md b/docs/references/autodoc.md similarity index 53% rename from .github/AUTODOC_GUIDE.md rename to docs/references/autodoc.md index 80c366fecf8f..bb1278becf86 100644 --- a/.github/AUTODOC_GUIDE.md +++ b/docs/references/autodoc.md @@ -1,47 +1,51 @@ # dmdoc -[DOCUMENTATION]: https://codedocs.paradisestation.org/ -[BYOND]: https://secure.byond.com/ - -[DMDOC]: https://github.com/SpaceManiac/SpacemanDMM/tree/master/crates/dmdoc - -[DMDOC] is a documentation generator for DreamMaker, the scripting language -of the [BYOND] game engine. It produces simple static HTML files based on +[dmdoc] is a documentation generator for DreamMaker, the scripting language of +the [BYOND] game engine. It produces simple static HTML files based on documented files, macros, types, procs, and vars. -We use **dmdoc** to generate [DOCUMENTATION] for our code, and that documentation -is automatically generated and built on every new commit to the master branch +We use **dmdoc** to generate [documentation] for our code, and that +documentation is automatically generated and built on every new commit to the +master branch + +This gives new developers a clickable reference [documentation] they can browse +to better help gain understanding of the Paradise codebase structure and api +reference. -This gives new developers a clickable reference [DOCUMENTATION] they can browse to better help -gain understanding of the Paradise codebase structure and api reference. +[documentation]: https://codedocs.paradisestation.org/ +[BYOND]: https://secure.byond.com/ +[dmdoc]: https://github.com/SpaceManiac/SpacemanDMM/tree/master/crates/dmdoc -## Documenting code on Paradise -We use block comments to document procs and classes, and we use `///` line comments -when documenting individual variables. +## Documenting Code On Paradise +We use block comments to document procs and classes, and we use `///` line +comments when documenting individual variables. -Documentation is not required at Paradise, but it is highly recommended that all new code be covered with DMdoc code, according to the [Specifications](#Specification) +Documentation is not required at Paradise, but it is highly recommended that all +new code be covered with DMdoc code, according to the +[Specifications](#specification). We also recommend that when you touch older code, you document the functions that you have touched in the process of updating that code ### Specification -A class *should* always be autodocumented, and all public functions *should* be documented +A class *should* always be auto-documented, and all public functions *should* be +documented. -All class level defined variables *should* be documented +All class level defined variables *should* be documented. -Internal functions *can* be documented, but may not be +Internal functions *can* be documented, but may not be. A public function is any function that a developer might reasonably call while using or interacting with your object. Internal functions are helper functions that your -public functions rely on to implement logic - +public functions rely on to implement logic. ### Documenting a proc When documenting a proc, we give a short one line description (as this is shown next to the proc definition in the list of all procs for a type or global namespace), then a longer paragraph which will be shown when the user clicks on the proc to jump to it's definition -``` + +```dm /** * Short description of the proc * @@ -53,14 +57,13 @@ the proc to jump to it's definition */ ``` -### Documenting a class -We first give the name of the class as a header, this can be omitted if the name is -just going to be the typepath of the class, as dmdoc uses that by default +### Documenting Classes +We first give the name of the class as a header, this can be omitted if the name +is just going to be the typepath of the class, as dmdoc uses that by default. +Then we give a short one-line description of the class. Finally we give a longer +multi paragraph description of the class and it's details. -Then we give a short oneline description of the class - -Finally we give a longer multi paragraph description of the class and it's details -``` +```dm /** * # Classname (Can be omitted if it's just going to be the typepath) * @@ -75,7 +78,8 @@ Finally we give a longer multi paragraph description of the class and it's detai ### Documenting a variable/define Give a short explanation of what the variable, in the context of the class, or define is. -``` + +```dm /// Type path of item to go in suit slot var/suit = null ``` @@ -89,20 +93,20 @@ that will also be rendered and added to the modules tree. The structure for these is deliberately not defined, so you can be as freeform and as wheeling as you would like. -[Here is a representative example of what you might write](https://codedocs.paradisestation.org/code/modules/keybindings/readme.html) - ## Special variables -You can use certain special template variables in DM DOC comments and they will be expanded -``` - [DEFINE_NAME] - Expands to a link to the define definition if documented - [/mob] - Expands to a link to the docs for the /mob class - [/mob/proc/Dizzy] - Expands to a link that will take you to the /mob class and anchor you to the dizzy proc docs - [/mob/var/stat] - Expands to a link that will take you to the /mob class and anchor you to the stat var docs -``` +You can use certain special template variables in DM DOC comments and they will +be expanded. + +- `[DEFINE_NAME]` expands to a link to the define definition if documented. +- `[/mob]` expands to a link to the docs for the /mob class. +- `[/mob/proc/Dizzy]` expands to a link that will take you to the /mob class and + anchor you to the dizzy proc docs. +- `[/mob/var/stat]` expands to a link that will take you to the /mob class and + anchor you to the stat var docs -You can customise the link name by using `[link name][link shorthand].` +You can customise the link name by using `[link name][link shorthand]`. -eg. `[see more about dizzy here] [/mob/proc/Dizzy]` +e.g. `[see more about dizzy here][/mob/proc/Dizzy]` This is very useful to quickly link to other parts of the autodoc code to expand -upon a comment made, or reasoning about code +upon a comment made, or reasoning about code. diff --git a/.github/USING_FEEDBACK_DATA.md b/docs/references/feedback_data.md similarity index 54% rename from .github/USING_FEEDBACK_DATA.md rename to docs/references/feedback_data.md index 889c14e5ff9f..4c94ce915371 100644 --- a/.github/USING_FEEDBACK_DATA.md +++ b/docs/references/feedback_data.md @@ -2,13 +2,18 @@ ## Introduction -`Feedback` is the name of the data storage system used for logging game statistics to the database. It is managed by `SSblackbox` and can be recorded in many formats. This guide will contain information on how to record feedback data properly, as well as what should and should not be recorded. +`Feedback` is the name of the data storage system used for logging game +statistics to the database. It is managed by `SSblackbox` and can be recorded in +many formats. This guide will contain information on how to record feedback data +properly, as well as what should and should not be recorded. ## Things you should and should not record -Feedback data can be useful, depending on how you use it. You need to be careful with what you record to make sure you are not saving useless data. Examples of good things to record: +Feedback data can be useful, depending on how you use it. You need to be careful +with what you record to make sure you are not saving useless data. Examples of +good things to record: -- Antagonist win/loss rates if a new gamemode or antagonist is being added +- Antagonist win/loss rates if a new game mode or antagonist is being added - Department performance (IE: Slime cores produced in science) - Basically anything which has actual meaning @@ -16,17 +21,21 @@ Examples of bad things to record: - Amount of times a wrench is used (Why) - Hours spent on the server (We have other means of that) -- Basically, just think about it and ask yourself "Is this actually useful to base game design around" +- Basically, just think about it and ask yourself "Is this actually useful to + base game design around" -Also note that feedback data **must** be anonymous. The only exception here is for data *anyone* on the server can see, such as round end antagonist reports. +Also note that feedback data **must** be anonymous. The only exception here is +for data *anyone* on the server can see, such as round end antagonist reports. ## Feedback Data Recording -Feedback data can be reocrded in 5 formats. `amount`, `associative`, `nested tally`, `tally` and `text`. +Feedback data can be recorded in 5 formats. `amount`, `associative`, `nested +tally`, `tally` and `text`. ### Amount -`amount` is the simplest form of feedback data recording. They are simply a numerical number which increase with each feedback increment. For example: +`amount` is the simplest form of feedback data recording. They are simply a +numerical number which increase with each feedback increment. For example: These DM calls: @@ -40,15 +49,23 @@ Will produce the following JSON: ```json { - "data":10 + "data": 10 } ``` -Notice the lack of any specific identification other than it being the value of the `data` key. Amounts are designed to be simple with minimal complexity, and are useful for logging statistics such as how many times one specific, distinct action is done (e.g.: How many MMIs have been filled). If you want to log multiple similar things (e.g.: How many mechas have been created, broken down by the mecha type), use a `tally` with a sub-key for each different mecha, instead of an amount with its own key per mecha. +Notice the lack of any specific identification other than it being the value of +the `data` key. Amounts are designed to be simple with minimal complexity, and +are useful for logging statistics such as how many times one specific, distinct +action is done (e.g.: How many MMIs have been filled). If you want to log +multiple similar things (e.g.: How many mechas have been created, broken down by +the mecha type), use a `tally` with a sub-key for each different mecha, instead +of an amount with its own key per mecha. ### Associative -`associative` is used to record text that's associated with multiple key-value pairs. (e.g: coordinates). Further calls to the same key will append a new list to existing data. For example: +`associative` is used to record text that's associated with multiple key-value +pairs. (e.g: coordinates). Further calls to the same key will append a new list +to existing data. For example: ```dm SSblackbox.record_feedback("associative", "example", 1, list("text" = "example", "path" = /obj/item, "number" = 4)) @@ -59,28 +76,39 @@ Will produce the following JSON: ```json { - "data":{ - "1":{ - "text":"example", - "path":"/obj/item", - "number":"4" + "data": { + "1": { + "text": "example", + "path": "/obj/item", + "number": "4" }, - "2":{ - "number":"7", - "text":"example", - "other text":"sample" + "2": { + "number": "7", + "text": "example", + "other text": "sample" } } } ``` -Notice how everything is cast to strings, and each new entry added has its index increased ("1", "2", etc). Also take note how the `increment` parameter is not used here. It does nothing to the data, and `1` is used just as the value for consistency. +Notice how everything is cast to strings, and each new entry added has its index +increased ("1", "2", etc). Also take note how the `increment` parameter is not +used here. It does nothing to the data, and `1` is used just as the value for +consistency. ### Nested Tally -`nested tally` is used to track the number of occurances of structured semi-relational values (e.g.: the results of arcade machines). You can think of it as a running total, with the key being a list of strings (rather than a single string), with elements incrementally identifying the entity in question. +`nested tally` is used to track the number of occurrences of structured +semi-relational values (e.g.: the results of arcade machines). You can think of +it as a running total, with the key being a list of strings (rather than a +single string), with elements incrementally identifying the entity in question. -Technically, the values are nested in a multi-dimensional array. The final element in the data list is used as the tracking key, and all prior elements are used for nesting. Further calls to the same key will add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position, and append the key and it's value if it doesn't exist already. This one is quite complicated, but an example is below: +Technically, the values are nested in a multi-dimensional array. The final +element in the data list is used as the tracking key, and all prior elements are +used for nesting. Further calls to the same key will add or subtract from the +saved value of the data key if it already exists in the same multi-dimensional +position, and append the key and it's value if it doesn't exist already. This +one is quite complicated, but an example is below: ```dm SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot")) @@ -115,7 +143,8 @@ Will produce the following JSON: #### NOTE -Tracking values associated with a number can't merge with a nesting value, trying to do so will append the list +Tracking values associated with a number can't merge with a nesting value, +trying to do so will append the list ```dm SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange")) @@ -145,11 +174,15 @@ Will produce the following JSON: } ``` -Avoid doing this, since having duplicate keys in JSON (data.fruit.orange) will break when parsing. +Avoid doing this, since having duplicate keys in JSON (data.fruit.orange) will +break when parsing. ### Tally -`tally` is used to track the number of occurances of multiple related values (e.g.: how many times each type of gun is fired). Further calls to the same key will add or subtract from the saved value of the data key if it already exists, and append the key and it's value if it doesn't exist. +`tally` is used to track the number of occurrences of multiple related values +(e.g.: how many times each type of gun is fired). Further calls to the same key +will add or subtract from the saved value of the data key if it already exists, +and append the key and it's value if it doesn't exist. ```dm SSblackbox.record_feedback("tally", "example", 1, "sample data") @@ -170,7 +203,10 @@ Will produce the following JSON: ### Text -`text` is used for simple single-string records (e.g.: the current chaplain religion). Further calls to the same key will append saved data unless the overwrite argument is true or it already exists. When encoded, calls made with overwrite will lack square brackets. +`text` is used for simple single-string records (e.g.: the current chaplain +religion). Further calls to the same key will append saved data unless the +overwrite argument is true or it already exists. When encoded, calls made with +overwrite will lack square brackets. ```dm SSblackbox.record_feedback("text", "example", 1, "sample text") @@ -189,11 +225,16 @@ Will produce the following JSON: } ``` -Note how `"sample text"` only appears once. `text` is a set with no duplicates, instead of a list with duplicates. Also take note how the `increment` parameter is not used here. It does nothing to the data, and `1` is used just as the value for consistency. +Note how `"sample text"` only appears once. `text` is a set with no duplicates, +instead of a list with duplicates. Also take note how the `increment` parameter +is not used here. It does nothing to the data, and `1` is used just as the value +for consistency. ## Feedback Versioning -If the logging content (i.e.: What data is logged) for a variable is ever changed, the version needs bumping. This can be done with the `versions` list on the subsystem definition itself. All values default to `1`. +If the logging content (i.e.: What data is logged) for a variable is ever +changed, the version needs bumping. This can be done with the `versions` list on +the subsystem definition itself. All values default to `1`. ```dm var/list/versions = list( @@ -202,4 +243,5 @@ var/list/versions = list( "gun_fired" = 2) ``` -If you are doing a type change (i.e.: Changing from a `tally` to a `nested tally`), **USE AN ENTIRELY NEW KEY NAME**. +If you are doing a type change (i.e.: Changing from a `tally` to a `nested +tally`), **USE AN ENTIRELY NEW KEY NAME**. diff --git a/docs/references/glossary.md b/docs/references/glossary.md new file mode 100644 index 000000000000..ab9f8bf59713 --- /dev/null +++ b/docs/references/glossary.md @@ -0,0 +1,263 @@ +Below is a glossary of common used terms along with their meanings relating to +the coding side of SS13. If you notice any missing terms of discrepancies in +descriptions, feel free to contribute to the list! Feel free to ask any +questions in `#coding-chat` on the discord. Non-coding related Glossary can be +found on the [wiki][]. More in depth information can be found at BYOND's +official [documentation][]. + +[wiki]: https://paradisestation.org/wiki/index.php?title=Glossary +[documentation]: https://secure.byond.com/docs/ref/#/DM/ + +## .DM +DreamMaker code files, or .dm files are the file format for BYOND source code. +These files must be "ticked" in the .dme file for them to be included in the +game. + +## .DMB +"DreamMaker Build" or DMB files are compiled DME files and are +used with Dream Daemon to run the server. + +## .DME +"DreamMaker Environment" or DME files are what BYOND uses to compile the game. +It is a list of all .dm files used in the game, if you add a new file you will +need to "Tick" it or add it to this file manually. + +## .DMI +DreamMaker images or DMI files is how BYOND stores images (also known as icons), +these can be edited with BYOND's tools or external tools. + +## .DMM +DreamMaker maps or DMM files is how BYOND stores maps. These can be edited with +BYOND's tools or something like [StrongDMM](#strongdmm) + +## Area +From the [BYOND documentation](https://secure.byond.com/docs/ref/#/area/): + +> "Areas are derived from /area. Regions on the map may be assigned to an area +by painting it onto the map. Areas off the map serve as rooms that objects may +enter and exit. For each area type defined, one area object is created at +runtime. So for areas on the map, all squares with the same area type belong to +the same instance of the area." + +In SS13, this is often used to delineate station departments and station systems +such as power and atmospherics networks. + +## Atmos +The atmospherics system in SS13, which is very often old and confusing and/or +broken code. + +## Atom +From the [BYOND documentation](https://secure.byond.com/docs/ref/#/atom/): + +> "The /atom object type is the ancestor of all mappable +objects in the game. The types /area, /turf, /obj, and /mob are all +derived from /atom. You should not create instances of /atom directly +but should use /area, /turf, /obj, and /mob for actual objects. The +/atom object type exists for the purpose of defining variables or +procedures that are shared by all of the other 'physical' objects. +These are also the only objects for which verbs may be accessible to the +user. /atom is derived from /datum, so it inherits the basic properties that +are shared by all DM objects." + +## Baseturf +An SS13 variable that saves the data of what is underneath if that that is +removed. For example, under station floors there would be a space turf and under +Lavaland turfs there would be lava. + +## Buff +A buff is a change to a gameplay mechanic that makes it more powerful or more +useful. Generally the opposite of a [nerf](#nerf). + +## Commit +A record of files changed and how they were changed, they are each assigned a +special ID called a hash that specifies the changes it makes. + +## Config +The config.toml file for changing things about your local server. You will need +to copy this from the config/example folder if you haven't already. + +## Datum +From the [BYOND documentation](https://secure.byond.com/docs/ref/#/datum/): + +> "The datum object is the ancestor of all other data types in DM. (The only +exceptions are currently `/world`, `/client`, `/list`, and `/savefile`, but +those will be brought into conformance soon.) That means that the variables and +procedures of /datum are inherited by all other types of objects." + +Datums are useful to represent abstractions that don't physically exist in the +game world, such as information about a spell or a Syndicate uplink item. They +are also useful for vars or procs that all other data-types use. + +## Define +A way of declaring variable either global (across the whole game) or in a whole +file using DM's `#DEFINE` macro syntax. They should always be found at the +beginning of a file. Defines should always be capitalized (LIKE_THIS) and if not +global should undefined at the end of a file. + +## Fastmos +Fast atmos, usually featuring explosive decomposition and lots of in game death. +Fastmos is not used on Paradise. + +## Garbage +The garbage collector handles items being deleted and allows them to clean up +references, this allows objects to delete much more efficiently. + +## Head Admin +Head of the admin team and overseeing overall changes and the direction for the +entire Paradise codebase and server. Contact them or the Balance team about +large changes or balance changes before making a PR, including map additions, +new roles, new antagonists, and other similar things. + +## Icondiffbot +A tool on GitHub that renders before and after images of BYOND icons. It can be +viewed on any PR by scrolling down to the checks section and clicking details +next to it. + +## LGTM +"Looks Good To Me", used during code reviews. + +## Local +Your copy of your remote repository on your local machine or computer. Commits +need to be published to your remote repo before they can be pushed to the +upstream repo for a pull request. + +## Maintainer +A no longer used title, previously used for people who made sure code is +quality. Maintainers were split up into the Balance Team, Design Team, and several +other groups. Check [PR #18000](https://github.com/ParadiseSS13/Paradise/pull/18000/) for more +information. + +## Map merge +Tools that automatically attempt to merge maps when merging master or +committing. Map merge is a work in progress and may require manual editing too. + +## Mapdiffbot +A tool on GitHub that renders before and after images of BYOND maps. It can be +viewed on any PR by scrolling down to the checks section and clicking details +next to it. + +## Master Controller +The Master Controller controls all subsystems of +the game, such as the [garbage collector](#garbage). + +## MC +Short for Short for [Master Controller](#master-controller). + +## Merge Master +The process of merging master into your PR's branch, often to update it. + +## Mob +Mobs are "mobile objects", these include players and animals. This does not +include stuff like conveyors. + +## Nerf +Nerfs are changes to a gameplay mechanic that make it less powerful or decreases +its utility, typically done for the sake of improving game balance and +enjoyability. Generally the opposite of a [buff](#buff). + +## NPFC +"No Player-Facing Changes", used in the changelog of a PR, most often in +refractors and exploit fixes. + +## Object +Objects are things you can interact with in game, including things that do not +move. This includes weapons, items, machinery (consoles and machines), and +several other things. + +## Origin +Typically another name for your [remote repo](#remote). + +## PR +Short for [Pull Request](#pull-request). + +## Proc +Procs or Procedures are block of code that only runs when it is called. These +are similar to something like functions in other languages. + +## Publish +Uploading your code from your local machine. + +## Pull Request +A request to the Paradise Github Repository for certain +changes to be made to the code of the game. This includes maps and sprites. + +## Pulling code +Pulling is transferring commits from the main repo to your remote repo, or from +your remote repository to your local repository. + +## Pushing code +Pushing is how you transfer commits from your repository to the Upstream repo. + +## qdel +A function, `qdel()`, which tells the [garbage collector](#garbage) to handle +destruction of an object. This should always be used over `del()`. + +## QDEL_NULL +A [qdel](#qdel) function which first nulls out a variable before telling the +garbage collector to handle it. + +## Remote +Your forked copy of the upstream repo that you have complete access over. your +clean copy of master and any published branches you've made can be found here. + +## Repo +Short for [repository](#repository). + +## Repository +A collection of code which tracks the commits and changes to it. There are three +main types you will find the upstream repository, your remote repository, and +your local repository. + +## Runechat Chat +Chat messages which appear above player's characters, a feature added by +[#14141](https://github.com/ParadiseSS13/Paradise/pull/14141/). Often a joke +about players now missing important things in the chat window since they no +longer have to look there for reading messages from people. + +## Runtime +Runtimes most often refer to runtime errors, which are errors that happen after +compiling and happen in game. + +## StrongDMM +A [robust mapping tool](https://github.com/SpaiR/StrongDMM/) that is highly +recommended over BYOND's DMM editor, as it is much quicker and has much more +options. Using any version below 2.0 makes your PR very unlikely to be accepted +as it messes with variables. + +## TGUI +A JavaScript based format for displaying an interface. It is +used for our user interfaces (except OOC stuff like admin panels), or +are planned to be converted to TGUI. TGUI uses InfernoJS (based off of +reactJS) which is an extension to JavaScript. More information can be +found at the [TGUI Tutorial][]. + +[TGUI Tutorial]: https://github.com/ParadiseSS13/Paradise/blob/master/tgui/docs/tutorial-and-examples.md + +## Turf +Turfs are floors, stuff like space, floors, carpets, or lava, or walls. This +does not include windows, as they are objects. + +## Upstream +The original repo that you have forked your remote repository from. For us, it +is [ParadiseSS13/Paradise](https://github.com/ParadiseSS13/Paradise/). + +## Var +A variable, used for temporarily storing data. For more +permanent data, check out [defines](#define). + +## Verb +A special type of proc, which is only available to mobs. + +## View Variables +An admin tool that can be used in game to view the variables of anything, giving +you more information about them. Very useful for debugging. + +## VSC +Short for [Visual Studio Code](https://code.visualstudio.com/). + +## VV +Short for [View Variables](#view-variables). + +## WYCI +"When You Code It", a joking response to someone asking when something will be +added to the game. diff --git a/docs/references/tick_order.md b/docs/references/tick_order.md new file mode 100644 index 000000000000..84116c536646 --- /dev/null +++ b/docs/references/tick_order.md @@ -0,0 +1,53 @@ +# BYOND Tick Order + +This document roughly describes the order in which BYOND performs operations in a given tick. + +The BYOND tick proceeds as follows: + +1. Procs sleeping via `walk()` are resumed (I don't know why these are first). + +2. Normal sleeping procs are resumed, in the order they went to sleep in the + first place. This is where the MC wakes up and processes subsystems. A + consequence of this is that the MC almost never resumes before other sleeping + procs, because it only goes to sleep for 1 tick 99% of the time, and 99% of + procs either go to sleep for less time than the MC (which guarantees that + they entered the sleep queue earlier when its time to wake up) and/or were + called synchronously from the MC's execution, almost all of the time the MC + is the last sleeping proc to resume in any given tick. This is good because + it means the MC can account for the cost of previous resuming procs in the + tick, and minimizes overtime. + +3. Control is passed to BYOND after all of our code's procs stop execution for this tick. + +4. A few small things happen in BYOND internals. + +5. SendMaps is called for this tick, which processes the game state for all + clients connected to the game and handles sending them changes in appearances + within their view range. This is expensive and takes up a significant portion + of our tick, about 0.45% per connected player as of 3/20/2022. This means + that with 50 players, 22.5% of our tick is being used up by just SendMaps, + after all of our code has stopped executing. That's only the average across + all rounds, for most high-pop rounds it can look like 0.6% of the tick per + player, which is 30% for 50 players. + +6. After SendMaps ends, client verbs sent to the server are executed, and it's + the last major step before the next tick begins. During the course of the + tick, a client can send a command to the server saying that they have + executed any verb. The actual code defined for that /verb/name() proc isn't + executed until this point, and the way the MC is designed makes this + especially likely to make verbs "overrun" the bounds of the tick they + executed in, stopping the other tick from starting and thus delaying the MC + firing in that tick. + +The master controller can derive how much of the tick was used in: procs +executing before it woke up (because of world.tick_usage), and SendMaps (because +of world.map_cpu, since this is a running average you cant derive the tick spent +on maptick on any particular tick). It cannot derive how much of the tick was +used for sleeping procs resuming after the MC ran, or for verbs executing after +SendMaps. + +It is for these reasons why you should heavily limit processing done in verbs. +While procs resuming after the MC are rare, verbs are not, and are much more +likely to cause overtime since they're literally at the end of the tick. If you +make a verb, try to offload any expensive work to the beginning of the next tick +via a verb management subsystem. diff --git a/html/browser/common.css b/html/browser/common.css index 669054092b4a..e902c16e74eb 100644 --- a/html/browser/common.css +++ b/html/browser/common.css @@ -9,7 +9,8 @@ body { hr { background-color: #40628a; - height: 1px; + height: 2px; + border: 0; } a, @@ -335,7 +336,8 @@ div.notice { clear: both; } .charPreview { - -ms-interpolation-mode: nearest-neighbor; + -ms-interpolation-mode: nearest-neighbor; /* Remove with Byond 516 */ + image-rendering: pixelated; width: 64px; height: 64px; } diff --git a/html/statbrowser.js b/html/statbrowser.js index 857350e49604..93adf792cb7b 100644 --- a/html/statbrowser.js +++ b/html/statbrowser.js @@ -405,7 +405,7 @@ function listedturf_fill_row(row, item_index) { // of the last entry. return function (e) { e.preventDefault(); - clickcatcher = '?src=' + object_info[1]; + clickcatcher = '?src=' + object_info[4] + ';m5src=' + object_info[1]; switch (e.button) { case 1: clickcatcher += ';statpanel_item_click=middle'; diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi new file mode 100644 index 000000000000..77aa89155322 Binary files /dev/null and b/icons/effects/random_spawners.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 2700cad40ebf..2b6d77c819b0 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index fb9738d28229..1af9d7b8e330 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/obj/closet.dmi b/icons/obj/closet.dmi index c9a4c513cf3c..7ee905462de2 100644 Binary files a/icons/obj/closet.dmi and b/icons/obj/closet.dmi differ diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000000..e5ea2885260c --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,88 @@ +site_name: Paradise Contributor Documentation + +theme: + name: material + icon: + logo: material/palm-tree + palette: + accent: custom + primary: custom + favicon: ./images/favicon.png + +extra: + social: + - icon: material/forum + name: Paradise Station Discussion Forum + link: https://www.paradisestation.org/forum + - icon: fontawesome/brands/wikipedia-w + name: Paradise Station Wiki + link: https://www.paradisestation.org/wiki + - icon: fontawesome/brands/github + name: Paradise Station GitHub + link: https://github.com/ParadiseSS13/Paradise + - icon: fontawesome/brands/discord + name: Paradise Station Discord + link: https://discord.gg/paradisess13 + - icon: fontawesome/brands/patreon + name: Paradise Station Patreon + link: https://www.patreon.com/ParadiseStation + generator: false + +plugins: + - gh-admonitions + +markdown_extensions: + - admonition + - attr_list + - smarty + - pymdownx.highlight: + use_pygments: false + - pymdownx.details + - pymdownx.superfences + - pymdownx.snippets + +hooks: + - 'docs/hooks/contributing_path.py' + +extra_javascript: + - 'javascripts/highlight.min.js' + - 'javascripts/highlight-dm.js' + - 'javascripts/init.js' + +extra_css: + - 'css/para.css' + - 'css/atom-one-dark.css' + +nav: + # TODO: Coding / Code Style Guide + # TODO: Mapping / Guide to Mapmerge + # TODO: Spriting + # TODO: SS13 for Experienced Coders (maybe) + + - 'Introduction': 'index.md' + - 'Code of Conduct': 'CODE_OF_CONDUCT.md' + - 'Contributing Guidelines': 'CONTRIBUTING.md' + + - 'Contributing': + - 'Getting Started': './contributing/getting_started.md' + - 'Reviewer Crash Course': './contributing/reviewer.md' + - 'Writing Quality PRs': './contributing/quality_prs.md' + + - 'Coding': + - 'Coding Quickstart': './coding/quickstart.md' + - 'Guide to Testing': './coding/testing_guide.md' + - 'Guide to Debugging': './coding/debugging.md' + - 'Style Guidelines': './coding/style_guidelines.md' + - 'Coding Requirements': './coding/coding_requirements.md' + - 'Testing Requirements': './coding/testing_requirements.md' + + - 'Mapping': + - 'Mapping Quickstart': './mapping/quickstart.md' + - 'Mapping Requirements': './mapping/requirements.md' + - 'Design Guidelines': './mapping/design.md' + + - 'References': + - 'Glossary': './references/glossary.md' + - 'Autodoc Guide': './references/autodoc.md' + - 'Using Feedback Data': './references/feedback_data.md' + - 'Tick Order': './references/tick_order.md' diff --git a/paradise.dme b/paradise.dme index 7d57b41f5a18..e45c0b1c98c6 100644 --- a/paradise.dme +++ b/paradise.dme @@ -215,6 +215,7 @@ #include "code\_globalvars\lists\flavor_misc.dm" #include "code\_globalvars\lists\fortunes.dm" #include "code\_globalvars\lists\keybindings_lists.dm" +#include "code\_globalvars\lists\maint_loot_tables.dm" #include "code\_globalvars\lists\misc_lists.dm" #include "code\_globalvars\lists\mob_lists.dm" #include "code\_globalvars\lists\names.dm" @@ -1011,15 +1012,23 @@ #include "code\game\objects\effects\effect_system\effects_water.dm" #include "code\game\objects\effects\spawners\airlock_spawner.dm" #include "code\game\objects\effects\spawners\bombspawner.dm" +#include "code\game\objects\effects\spawners\decorative_spawners.dm" +#include "code\game\objects\effects\spawners\depot_spawners.dm" #include "code\game\objects\effects\spawners\detgun_spawner.dm" #include "code\game\objects\effects\spawners\gibspawner.dm" #include "code\game\objects\effects\spawners\grouped_spawner.dm" #include "code\game\objects\effects\spawners\lootdrop.dm" #include "code\game\objects\effects\spawners\mess_spawners.dm" #include "code\game\objects\effects\spawners\random_barrier.dm" -#include "code\game\objects\effects\spawners\random_spawners.dm" +#include "code\game\objects\effects\spawners\turf_spawners.dm" #include "code\game\objects\effects\spawners\vaultspawner.dm" #include "code\game\objects\effects\spawners\windowspawner.dm" +#include "code\game\objects\effects\spawners\random\engineering_spawners.dm" +#include "code\game\objects\effects\spawners\random\food_spawners.dm" +#include "code\game\objects\effects\spawners\random\maint_loot_spawners.dm" +#include "code\game\objects\effects\spawners\random\misc_spawners.dm" +#include "code\game\objects\effects\spawners\random\random_spawner.dm" +#include "code\game\objects\effects\spawners\random\trash_spawners.dm" #include "code\game\objects\effects\temporary_visuals\clockcult.dm" #include "code\game\objects\effects\temporary_visuals\cult_visuals.dm" #include "code\game\objects\effects\temporary_visuals\explosion_temp_visuals.dm" @@ -1620,6 +1629,7 @@ #include "code\modules\awaymissions\mission_code\shuttle_shadow.dm" #include "code\modules\awaymissions\mission_code\ghost_role_spawners\golems.dm" #include "code\modules\awaymissions\mission_code\ghost_role_spawners\oldstation_spawns.dm" +#include "code\modules\awaymissions\mission_code\ruins\abandoned_engi_sat.dm" #include "code\modules\awaymissions\mission_code\ruins\deepstorage.dm" #include "code\modules\awaymissions\mission_code\ruins\derelict5.dm" #include "code\modules\awaymissions\mission_code\ruins\gps_ruin.dm" @@ -2690,6 +2700,7 @@ #include "code\modules\redis\redis_callback.dm" #include "code\modules\redis\redis_message.dm" #include "code\modules\redis\callbacks\asay_callback.dm" +#include "code\modules\redis\callbacks\dev_callback.dm" #include "code\modules\redis\callbacks\msay_callback.dm" #include "code\modules\redis\callbacks\server_messages_callback.dm" #include "code\modules\research\circuitprinter.dm" diff --git a/strings/sillytips.txt b/strings/sillytips.txt index c5bc6f25ab3b..2d118c7a5b94 100644 --- a/strings/sillytips.txt +++ b/strings/sillytips.txt @@ -32,3 +32,4 @@ HONK. Solars probably give radiation poisoning or something, considering the other power sources are deadly in their own ways. Nanotrasen would like to remind you that the popular "Deathsquad" show is completely fictional. The Deathsquad is not real. Making any suggestion to the contrary is grounds for immediate termination. It's rumored that you can recolor the fur on station pets in washing machines by also throwing in a crayon. +Smoking is cool, but you have to light that cigarette before you can use it! How many ways to light a cigarette can you name? diff --git a/strings/tips.txt b/strings/tips.txt index 4fab8345756f..9aa338604e80 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -187,6 +187,7 @@ As a Terror Spider, sometimes quickly pulling an enemy into a web can ensure vic Remote signalers are extremely versatile. They can be used in bombs, hacking, counteracting anomalies, and much more. Get creative! Need to perform surgery? No surgery kit? No worries! You can use a wide variety of improvised tools to perform ghetto surgery! For example, using a wrench as a stand-in for a bone setter, or a cigarette as a cautery. You can even use alcohol as a makeshift painkiller. Just be aware that these tools may not work as well as proper ones. If you examine something twice in quick succession, you will perform an extended examination. Many items have extended descriptions that give you more information about the world. +You can dip cigarettes into beakers or inject reagents into them. This can be used to infuse cigarettes with powerful medications or poisons which will slowly release over time. Welding fuel and plasma make them explode when lit! You can swap floor tiles by holding a crowbar in one hand and a stack of tiles in the other. When hacking doors, cutting and mending a "test light wire" will restore power to the door. When crafting most items, you can either manually combine parts or use the crafting menu. diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md index af744b617937..3865c5c1724e 100644 --- a/tgui/docs/component-reference.md +++ b/tgui/docs/component-reference.md @@ -8,53 +8,55 @@ This table of contents must be manually maintained. Make sure to add new items to this list if you document new components. --> -- [General Concepts](#general-concepts) -- [`tgui/components`](#tguicomponents) - - [`AnimatedNumber`](#animatednumber) - - [`BlockQuote`](#blockquote) - - [`Box`](#box) - - [`Button`](#button) - - [`Button.Checkbox`](#buttoncheckbox) - - [`Button.Confirm`](#buttonconfirm) - - [`Button.Input`](#buttoninput) - - [`ByondUi`](#byondui) - - [`Collapsible`](#collapsible) - - [`ColorBox`](#colorbox) - - [`Dimmer`](#dimmer) - - [`Divider`](#divider) - - [`Dropdown`](#dropdown) - - [`Flex`](#flex) - - [`Flex.Item`](#flexitem) - - [`Grid`](#grid) - - [`Grid.Column`](#gridcolumn) - - [`Icon`](#icon) - - [`Icon.Stack`](#iconstack) - - [`ImageButton`](#imagebutton) - - [`ImageButton.Item`](#imagebuttonitem) - - [`Input`](#input) - - [`Knob`](#knob) - - [`LabeledControls`](#labeledcontrols) - - [`LabeledControls.Item`](#labeledcontrolsitem) - - [`LabeledList`](#labeledlist) - - [`LabeledList.Item`](#labeledlistitem) - - [`LabeledList.Divider`](#labeledlistdivider) - - [`Modal`](#modal) - - [`NoticeBox`](#noticebox) - - [`NumberInput`](#numberinput) - - [`ProgressBar`](#progressbar) - - [`RoundGauge`](#roundgauge) - - [`Section`](#section) - - [`Slider`](#slider) - - [`Stack`](#stack) - - [`Table`](#table) - - [`Table.Row`](#tablerow) - - [`Table.Cell`](#tablecell) - - [`Tabs`](#tabs) - - [`Tabs.Tab`](#tabstab) - - [`Tooltip`](#tooltip) -- [`tgui/layouts`](#tguilayouts) - - [`Window`](#window) - - [`Window.Content`](#windowcontent) +- [Component Reference](#component-reference) + - [General Concepts](#general-concepts) + - [`tgui/components`](#tguicomponents) + - [`AnimatedNumber`](#animatednumber) + - [`BlockQuote`](#blockquote) + - [`Box`](#box) + - [`Button`](#button) + - [`Button.Checkbox`](#buttoncheckbox) + - [`Button.Confirm`](#buttonconfirm) + - [`Button.Input`](#buttoninput) + - [`ByondUi`](#byondui) + - [`Collapsible`](#collapsible) + - [`ColorBox`](#colorbox) + - [`Dimmer`](#dimmer) + - [`Divider`](#divider) + - [`Dropdown`](#dropdown) + - [`Flex`](#flex) + - [`Flex.Item`](#flexitem) + - [`Grid`](#grid) + - [`Grid.Column`](#gridcolumn) + - [`Icon`](#icon) + - [`Icon.Stack`](#iconstack) + - [`ImageButton`](#imagebutton) + - [`Input`](#input) + - [`Knob`](#knob) + - [`Popper`](#popper) + - [`LabeledControls`](#labeledcontrols) + - [`LabeledControls.Item`](#labeledcontrolsitem) + - [`LabeledList`](#labeledlist) + - [`LabeledList.Item`](#labeledlistitem) + - [`LabeledList.Divider`](#labeledlistdivider) + - [`Modal`](#modal) + - [`NoticeBox`](#noticebox) + - [`NumberInput`](#numberinput) + - [`ProgressBar`](#progressbar) + - [`RoundGauge`](#roundgauge) + - [`Section`](#section) + - [`Slider`](#slider) + - [`Stack`](#stack) + - [`Stack.Item`](#stackitem) + - [`Table`](#table) + - [`Table.Row`](#tablerow) + - [`Table.Cell`](#tablecell) + - [`Tabs`](#tabs) + - [`Tabs.Tab`](#tabstab) + - [`Tooltip`](#tooltip) + - [`tgui/layouts`](#tguilayouts) + - [`Window`](#window) + - [`Window.Content`](#windowcontent) ## General Concepts @@ -550,63 +552,35 @@ Renders children icons on top of each other in order to make your own icon. ### `ImageButton` A Robust button is specifically for sticking a picture in it. -Has support for base64, spritesheets and URLs. **Props:** - See inherited props: [Box](#box) -- `asset: boolean` - Enables spritesheets support. -- `vertical: boolean` - Makes the button a inlined vertical rectangle. -- `color: string` - By default, the button is semi-transparent. You can change the overall colour, -all colours are available in KitchenSink in the corresponding section. -- `title: string` - The top text, it will always be bold, and also adds a divider between title and content. -Disabled if there is no content. -- `content: string|any` - All main content, usually text, but you can put in other components if you like. -Makes the vertical button square if empty. -- `selected: boolean` - Makes button selected (green) if true. -- `disabled: boolean` - Makes button disabled (red) if true. Also disables onClick. -- `disabledContent: string` - If button disabled and disabledContent filled, it will be used instead content. -- `image: string` - Base64 image, simple. Disabled if asset support enabled. -- `imageUrl: string` - PNG image or other asset. Make sure you use existing simple asset! Example: imageUrl={'image.png'} -- `imageAsset: string` - If you have enabled asset support, write here which spritesheet to use. -Example: imageAsset={'spritesheet_name64x64'} -- `imageSize: string` - Sets the size of the image and adjusts the size of the button itself accordingly. -Example: imageSize={'64px'} -- `tooltip: string` - A fancy, boxy tooltip, which appears when hovering -over the button. -- `tooltipPosition: string` - Position of the tooltip. See [`Popper`](#Popper) for valid options. -- `ellipsis: boolean` - If button width is constrained, button text will -be truncated with an ellipsis. Be careful however, because this prop breaks -the baseline alignment. -- `children: ImageButton.Item|any` - Items that are added to the right of the horizontal button. -- `onClick: function` - Called when element is clicked. Also enables hover effects. - -### `ImageButton.Item` - -Additional button/s for ImageButton. - -> Try not to add ImageButton.Item in large quantities. They reduce rendering speed very much -> Available only in horizontal mode, if you try add it to vertical, you're gonna be disappointed - -**Props:** -- See inherited props: [Box](#box) -- `color: string` - By default, the button is semi-transparent. You can change the overall colour, -all colours are available in KitchenSink in the corresponding section. -- `content: string|any` - All main content, usually text, but you can put in other components if you like. -Try to not make it too long. -- `selected: boolean` - Makes button selected (green) if true. -- `disabled: boolean` - Makes button disabled (red) if true. Also disables onClick. -- `disabledContent: string` - If button disabled and disabledContent filled, it will be used instead content. +- `asset: string[]` - Asset cache. Example: `asset={`assetname32x32, ${thing.key}`}` +- `base64: string` - Classic way to put images. Example: `base64={thing.image}` +- `buttons: any` - Special section for any component, or, content. + Quite a small area at the bottom of the image in non-fluid mode. + Has a style overrides, best to use [Button](#button) inside. +- `buttonsAlt: boolean` - Enables alternative buttons layout. + With fluid, makes buttons like a humburger. + Without, moves it to top, and disables pointer-events. +- `children: any` - Content under image. +- `className: string` - Applies a CSS class to the element. +- `color: string` - Color of the button, but without `transparent`; see [Button](#button) +- `disabled: boolean` - Makes button disabled and dark red if true. + Also disables onClick & onRightClick. +- `selected: boolean` - Makes button selected and green if true. +- `fluid: boolean` - Changes the layout of the button, making it fill the entire horizontally available space. + Allows the use of `title` +- `imageSize: number` - Parameter responsible for the size of the image, component and standard "stubs". + Measured in pixels. `imageSize={64}` = 64px. +- `imageSrc: string` - Prop `src` of . Example: `imageSrc={resolveAsset(thing.image)}` +- `onClick: (e) => void` - Called when button is clicked with LMB. +- `onRightClick: (e) => void` - Called when button is clicked with RMB. +- `title: string` - Requires `fluid` for work. Bold text with divider betwen content. - `tooltip: string` - A fancy, boxy tooltip, which appears when hovering over the button. - `tooltipPosition: string` - Position of the tooltip. See [`Popper`](#Popper) for valid options. -- `icon: string` - Adds an icon to the button. By default it will be under content. -- `iconColor: string` - Paints icon if it used. -- `iconPosition: string` - You can make an icon above the content. -Example: iconPosition={'top'} -- `iconSize: number` - Adjusts the size of the icon. -- `children: any` - Similar to content. -- `onClick: function` - Called when element is clicked. ### `Input` diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index 3f7f58a61b8d..17b397914a1a 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -4,7 +4,7 @@ "version": "4.5.1-paradise", "type": "module", "dependencies": { - "axios": "^1.7.2", + "axios": "^1.7.4", "glob": "^11.0.0", "source-map": "^0.7.4", "stacktrace-parser": "^0.1.10", diff --git a/tgui/packages/tgui-panel/chat/constants.js b/tgui/packages/tgui-panel/chat/constants.js index 632b0705cb4e..35655bee0494 100644 --- a/tgui/packages/tgui-panel/chat/constants.js +++ b/tgui/packages/tgui-panel/chat/constants.js @@ -33,6 +33,7 @@ export const MESSAGE_TYPE_MENTORPM = 'mentorpm'; export const MESSAGE_TYPE_COMBAT = 'combat'; export const MESSAGE_TYPE_ADMINCHAT = 'adminchat'; export const MESSAGE_TYPE_MENTORCHAT = 'mentorchat'; +export const MESSAGE_TYPE_DEVCHAT = 'devchat'; export const MESSAGE_TYPE_EVENTCHAT = 'eventchat'; export const MESSAGE_TYPE_ADMINLOG = 'adminlog'; export const MESSAGE_TYPE_ATTACKLOG = 'attacklog'; @@ -124,6 +125,13 @@ export const MESSAGE_TYPES = [ selector: '.mentor_channel', admin: true, }, + { + type: MESSAGE_TYPE_DEVCHAT, + name: 'Developer Chat', + description: 'DEVSAY messages', + selector: '.dev_channel', + admin: true, + }, { type: MESSAGE_TYPE_ADMINLOG, name: 'Admin Log', diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index 662ef12443cc..dd77f9a37c18 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -405,6 +405,16 @@ em { font-weight: bold; } +.dev_channel { + color: #775bff; + font-weight: bold; +} + +.dev_channel_admin { + color: #a35cff; + font-weight: bold; +} + .djradio { color: #996600; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index c9dba6ae2700..84a06ada436e 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -419,6 +419,16 @@ em { font-weight: bold; } +.dev_channel { + color: #775bff; + font-weight: bold; +} + +.dev_channel_admin { + color: #a35cff; + font-weight: bold; +} + .djradio { color: #663300; } diff --git a/tgui/packages/tgui-say/ChannelIterator.test.ts b/tgui/packages/tgui-say/ChannelIterator.test.ts index 49c96d3e403b..ad91565156cb 100644 --- a/tgui/packages/tgui-say/ChannelIterator.test.ts +++ b/tgui/packages/tgui-say/ChannelIterator.test.ts @@ -14,7 +14,7 @@ describe('ChannelIterator', () => { expect(channelIterator.next()).toBe('Me'); expect(channelIterator.next()).toBe('OOC'); expect(channelIterator.next()).toBe('LOOC'); - expect(channelIterator.next()).toBe('Say'); // Admin, Mentor, and Dsay are blacklisted so should be skipped + expect(channelIterator.next()).toBe('Say'); // Admin, Mentor, Dsay, and Dev are blacklisted so should be skipped }); it('should set a channel properly', () => { diff --git a/tgui/packages/tgui-say/ChannelIterator.ts b/tgui/packages/tgui-say/ChannelIterator.ts index d38c222e164e..7c84ff3b9d0d 100644 --- a/tgui/packages/tgui-say/ChannelIterator.ts +++ b/tgui/packages/tgui-say/ChannelIterator.ts @@ -1,4 +1,4 @@ -export type Channel = 'Say' | 'Radio' | 'Whisper' | 'Me' | 'OOC' | 'LOOC' | 'Mentor' | 'Admin' | 'Dsay'; +export type Channel = 'Say' | 'Radio' | 'Whisper' | 'Me' | 'OOC' | 'LOOC' | 'Mentor' | 'Admin' | 'Dsay' | 'Dev'; /** * ### ChannelIterator @@ -8,9 +8,20 @@ export type Channel = 'Say' | 'Radio' | 'Whisper' | 'Me' | 'OOC' | 'LOOC' | 'Men */ export class ChannelIterator { private index: number = 0; - private readonly channels: Channel[] = ['Say', 'Radio', 'Whisper', 'Me', 'OOC', 'LOOC', 'Mentor', 'Admin', 'Dsay']; - private readonly blacklist: Channel[] = ['Mentor', 'Admin', 'Dsay']; - private readonly quiet: Channel[] = ['OOC', 'LOOC', 'Mentor', 'Admin', 'Dsay']; + private readonly channels: Channel[] = [ + 'Say', + 'Radio', + 'Whisper', + 'Me', + 'OOC', + 'LOOC', + 'Mentor', + 'Admin', + 'Dsay', + 'Dev', + ]; + private readonly blacklist: Channel[] = ['Mentor', 'Admin', 'Dsay', 'Dev']; + private readonly quiet: Channel[] = ['OOC', 'LOOC', 'Mentor', 'Admin', 'Dsay', 'Dev']; public next(): Channel { if (this.blacklist.includes(this.channels[this.index])) { diff --git a/tgui/packages/tgui-say/styles/colors.scss b/tgui/packages/tgui-say/styles/colors.scss index ef0b25f59917..7437d5a423d1 100644 --- a/tgui/packages/tgui-say/styles/colors.scss +++ b/tgui/packages/tgui-say/styles/colors.scss @@ -22,6 +22,7 @@ $_channel_map: ( 'Me': #5975da, 'Med': #57b8f0, 'Mentor': #d6c208, + 'Dev': #61b413, 'OOC': #cca300, 'Proc': #b84f92, 'R-Ear': #a4bad6, diff --git a/tgui/packages/tgui/components/Button.tsx b/tgui/packages/tgui/components/Button.tsx index d63cf2d8f9b5..21cb12512b94 100644 --- a/tgui/packages/tgui/components/Button.tsx +++ b/tgui/packages/tgui/components/Button.tsx @@ -17,6 +17,7 @@ const logger = createLogger('Button'); export type ButtonProps = BoxProps & { fluid?: boolean; + translucent?: boolean; icon?: string; iconRotation?: number; iconSpin?: BooleanLike; @@ -42,6 +43,7 @@ export const Button = (props: ButtonProps) => { const { className, fluid, + translucent, icon, iconRotation, iconSpin, @@ -84,15 +86,17 @@ export const Button = (props: ButtonProps) => { className={classes([ 'Button', fluid && 'Button--fluid', - disabled && 'Button--disabled', - selected && 'Button--selected', + disabled && 'Button--disabled' + (translucent ? '--translucent' : ''), + selected && 'Button--selected' + (translucent ? '--translucent' : ''), hasContent && 'Button--hasContent', ellipsis && 'Button--ellipsis', circular && 'Button--circular', compact && 'Button--compact', iconRight && 'Button--iconRight', multiLine && 'Button--multiLine', - color && typeof color === 'string' ? 'Button--color--' + color : 'Button--color--default', + color && typeof color === 'string' + ? 'Button--color--' + color + (translucent ? '--translucent' : '') + : 'Button--color--default' + (translucent ? '--translucent' : ''), className, ])} tabIndex={!disabled && '0'} diff --git a/tgui/packages/tgui/components/ImageButton.js b/tgui/packages/tgui/components/ImageButton.js deleted file mode 100644 index 576c62b7bf95..000000000000 --- a/tgui/packages/tgui/components/ImageButton.js +++ /dev/null @@ -1,204 +0,0 @@ -/** - * @file - * @copyright 2024 Aylong (https://github.com/AyIong) - * @license MIT - */ - -import { resolveAsset } from '../assets'; -import { classes, pureComponentHooks } from 'common/react'; -import { computeBoxClassName, computeBoxProps } from './Box'; -import { Icon } from './Icon'; -import { Tooltip } from './Tooltip'; - -export const ImageButton = (props) => { - const { - className, - asset, - color, - title, - vertical, - content, - selected, - disabled, - disabledContent, - image, - imageUrl, - imageAsset, - imageSize, - tooltip, - tooltipPosition, - ellipsis, - children, - onClick, - ...rest - } = props; - rest.onClick = (e) => { - if (!disabled && onClick) { - onClick(e); - } - }; - let buttonContent = ( -
-
- {asset ? ( -
- ) : ( - - )} -
- {content && - (vertical ? ( -
- {disabled && disabledContent ? disabledContent : content} -
- ) : ( -
- {title && ( -
- {title} -
-
- )} -
{content}
-
- ))} -
- ); - - if (tooltip) { - buttonContent = ( - - {buttonContent} - - ); - } - - return ( -
- {buttonContent} - {children} -
- ); -}; - -ImageButton.defaultHooks = pureComponentHooks; - -/** - * That's VERY fucking expensive thing! - * Use it only in places, where it really needed. - * Otherwise, the window opening time may increase by a third! - * Most of the blame is on Icon. - * Maybe it's also because I'm a bit crooked. - * (с) Aylong - */ -export const ImageButtonItem = (props) => { - const { - className, - color, - content, - horizontal, - selected, - disabled, - disabledContent, - tooltip, - tooltipPosition, - icon, - iconColor, - iconPosition, - iconRotation, - iconSize, - onClick, - children, - ...rest - } = props; - rest.onClick = (e) => { - if (!disabled && onClick) { - onClick(e); - } - }; - let itemContent = ( -
-
-
- {icon && (iconPosition === 'top' || iconPosition === 'left') && ( - - )} -
- {disabled && disabledContent ? disabledContent : content} - {children} -
- {icon && !(iconPosition === 'top' || iconPosition === 'left') && ( - - )} -
-
-
- ); - if (tooltip) { - itemContent = ( - - {itemContent} - - ); - } - - return itemContent; -}; - -ImageButton.Item = ImageButtonItem; diff --git a/tgui/packages/tgui/components/ImageButton.tsx b/tgui/packages/tgui/components/ImageButton.tsx new file mode 100644 index 000000000000..4b1b439e102e --- /dev/null +++ b/tgui/packages/tgui/components/ImageButton.tsx @@ -0,0 +1,185 @@ +/** + * @file + * @copyright 2024 Aylong (https://github.com/AyIong) + * @license MIT + */ + +import { Placement } from '@popperjs/core'; + +import { InfernoNode } from 'inferno'; +import { BooleanLike, classes } from 'common/react'; +import { BoxProps, computeBoxProps } from './Box'; +import { Icon } from './Icon'; +import { Stack } from './Stack'; +import { Tooltip } from './Tooltip'; + +type Props = Partial<{ + /** Asset cache. Example: `asset={`assetname32x32, ${thing.key}`}` */ + asset: string[]; + /** Classic way to put images. Example: `base64={thing.image}` */ + base64: string; + /** + * Special container for buttons. + * You can put any other component here. + * Has some special stylings! + * Example: `buttons={}` + */ + buttons: InfernoNode; + /** Enables alternate buttons container. Disables pointer-events on buttons if non-fluid. */ + buttonsAlt: boolean; + /** Content under image. Or on the right if fluid. */ + children: InfernoNode; + /** Applies a CSS class to the element. */ + className: string; + /** Color of the button. See [Button](#button) but without `transparent`. */ + color: string; + /** Makes button disabled and dark red if true. Also disables onClick. */ + disabled: BooleanLike; + /** + * Changes the layout of the button, making it fill the entire horizontally available space. + * Allows the use of `title` + */ + fluid: boolean; + /** Parameter responsible for the size of the image, component and standard "stubs". */ + imageSize: number; + /** Prop `src` of . Example: `imageSrc={resolveAsset(thing.image}` */ + imageSrc: string; + /** Called when button is clicked with LMB. */ + onClick: (e: any) => void; + /** Called when button is clicked with RMB. */ + onRightClick: (e: any) => void; + /** Makes button selected and green if true. */ + selected: BooleanLike; + /** Requires `fluid` for work. Bold text with divider betwen content. */ + title: string; + /** A fancy, boxy tooltip, which appears when hovering over the button */ + tooltip: InfernoNode; + /** Position of the tooltip. See [`Popper`](#Popper) for valid options. */ + tooltipPosition: Placement; +}> & + BoxProps; + +export const ImageButton = (props: Props) => { + const { + asset, + base64, + buttons, + buttonsAlt, + children, + className, + color, + disabled, + fluid, + imageSize = 64, + imageSrc, + onClick, + onRightClick, + selected, + title, + tooltip, + tooltipPosition, + ...rest + } = props; + + const getFallback = (iconName: string, iconSpin: boolean) => { + return ( + + + + + + ); + }; + + let buttonContent = ( +
{ + if (!disabled && onClick) { + onClick(event); + } + }} + onContextMenu={(event) => { + event.preventDefault(); + if (!disabled && onRightClick) { + onRightClick(event); + } + }} + style={{ width: !fluid ? `calc(${imageSize}px + 0.5em + 2px)` : 'auto' }} + > +
+ {(base64 || imageSrc) && !asset ? ( + + ) : asset ? ( + /* Not a cause assets made some shit with it on Byond 516 */ +
+ ) : ( + getFallback('question', false) + )} +
+ {fluid ? ( +
+ {title && {title}} + {children && {children}} +
+ ) : ( + children && ( + + {children} + + ) + )} +
+ ); + + if (tooltip) { + buttonContent = ( + + {buttonContent} + + ); + } + + return ( +
+ {buttonContent} + {buttons && ( +
+ {buttons} +
+ )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster.tsx b/tgui/packages/tgui/interfaces/ChemMaster.tsx index 5afe8224c584..bff2b9ffad0f 100644 --- a/tgui/packages/tgui/interfaces/ChemMaster.tsx +++ b/tgui/packages/tgui/interfaces/ChemMaster.tsx @@ -459,7 +459,7 @@ const ChemMasterProductionGeneric = (props: { productionData: ProductionData }, act('set_sprite_style', { production_mode: modeId, style: id })} selected={set_sprite === id} /> diff --git a/tgui/packages/tgui/interfaces/CoinMint.tsx b/tgui/packages/tgui/interfaces/CoinMint.tsx index f99f8ce2590a..e0af52949ced 100644 --- a/tgui/packages/tgui/interfaces/CoinMint.tsx +++ b/tgui/packages/tgui/interfaces/CoinMint.tsx @@ -66,9 +66,9 @@ export const CoinMint = (props, context) => { key={material.id} bold inline + translucent m={0.2} textAlign={'center'} - color={'translucent'} selected={material.id === data.chosenMaterial} tooltip={material.name} content={ diff --git a/tgui/packages/tgui/interfaces/MedicalRecords.js b/tgui/packages/tgui/interfaces/MedicalRecords.js index ed070d52f826..19eff041ae82 100644 --- a/tgui/packages/tgui/interfaces/MedicalRecords.js +++ b/tgui/packages/tgui/interfaces/MedicalRecords.js @@ -167,25 +167,17 @@ const MedicalRecordsMaintenance = (_properties, context) => {
- + } > - act('purchase', { 'purchase': prize.itemID })} - /> + {prize.desc} ); })} diff --git a/tgui/packages/tgui/interfaces/RCD.js b/tgui/packages/tgui/interfaces/RCD.js index 963623f86a2e..1211bdbb2b7a 100644 --- a/tgui/packages/tgui/interfaces/RCD.js +++ b/tgui/packages/tgui/interfaces/RCD.js @@ -243,11 +243,10 @@ const AirlockTypeList = (props, context) => { {doors_filtered.map((entry, i) => ( - {
+
setTranslucent(!translucent)} content="Translucent" /> + } + > {COLORS_STATES.map((color) => ( -
+
{COLORS_SPECTRUM.map((color) => ( -
+
{COLORS_SPECTRUM.map((color) => ( {color} ))} - -
+ + ); }; diff --git a/tgui/packages/tgui/stories/ImageButton.stories.js b/tgui/packages/tgui/stories/ImageButton.stories.js index 70ffe9fc1773..29adbe2292f7 100644 --- a/tgui/packages/tgui/stories/ImageButton.stories.js +++ b/tgui/packages/tgui/stories/ImageButton.stories.js @@ -31,171 +31,123 @@ const COLORS_SPECTRUM = [ const COLORS_STATES = ['good', 'average', 'bad', 'black', 'white']; const Story = (props, context) => { + const [fluid1, setFluid1] = useLocalState(context, 'fluid1', true); + const [fluid2, setFluid2] = useLocalState(context, 'fluid2', false); + const [fluid3, setFluid3] = useLocalState(context, 'fluid3', false); const [disabled, setDisabled] = useLocalState(context, 'disabled', false); - const [onClick, setOnClick] = useLocalState(context, 'onClick', true); - const [vertical1, setVertical1] = useLocalState(context, 'vertical1', true); - const [vertical2, setVertical2] = useLocalState(context, 'vertical2', true); - const [vertical3, setVertical3] = useLocalState(context, 'vertical3', false); + const [selected, setSelected] = useLocalState(context, 'selected', false); + const [addImage, setAddImage] = useLocalState(context, 'addImage', false); + const [base64, setbase64] = useLocalState(context, 'base64', ''); const [title, setTitle] = useLocalState(context, 'title', 'Image Button'); - const [content, setContent] = useLocalState(context, 'content', 'Image is a LIE!'); - const [itemContent, setItemContent] = useLocalState(context, 'itemContent', 'Second Button'); - const [itemIcon, setItemIcon] = useLocalState(context, 'itemIcon', 'face-smile'); - - const [itemIconPos, setItemIconPos] = useLocalState(context, 'itemIconPos', 'default'); - - const [itemIconSize, setItemIconSize] = useLocalState(context, 'itemIconSize', 2); - + const [content, setContent] = useLocalState(context, 'content', 'You can put anything in there'); const [imageSize, setImageSize] = useLocalState(context, 'imageSize', 64); - const toggleVertical1 = () => { - setVertical1(!vertical1); - }; - - const toggleVertical2 = () => { - setVertical2(!vertical2); - }; - - const toggleVertical3 = () => { - setVertical3(!vertical3); - }; - - const toggleDisabled = () => { - setDisabled(!disabled); - }; - - const toggleOnClick = () => { - setOnClick(!onClick); - }; - return ( <>
- - setTitle(value)} /> - - - setContent(value)} /> - - - setImageSize(value)} - /> - + {addImage ? ( + + setbase64(value)} /> + + ) : ( + <> + + setTitle(value)} /> + + + setContent(value)} /> + + + setImageSize(value)} + /> + + + )} - + + + + + setFluid1(!fluid1)}> + Fluid + + - + setDisabled(!disabled)}> + Disabled + - + setSelected(!selected)}> + Selected + - - - - setItemContent(value)} /> - - - setItemIcon(value)} /> - - - setItemIconPos(value)} /> - - - setItemIconSize(value)} - /> - - - setAddImage(!addImage)} + > + Add Image + } - imageSize={`${imageSize}px`} - onClick={onClick ? () => 'false' : ''} > - {!vertical3 && ( - - )} + {content}
} + buttons={ + setFluid2(!fluid2)}> + Fluid + + } > {COLORS_STATES.map((color) => ( - 'false' : ''} - /> + + {color} + ))}
} + buttons={ + setFluid3(!fluid3)}> + Fluid + + } > {COLORS_SPECTRUM.map((color) => ( - 'false' : ''} - /> + + {color} + ))}
diff --git a/tgui/packages/tgui/styles/components/Button.scss b/tgui/packages/tgui/styles/components/Button.scss index 550a686b46b6..a6cd74c873fb 100644 --- a/tgui/packages/tgui/styles/components/Button.scss +++ b/tgui/packages/tgui/styles/components/Button.scss @@ -9,6 +9,7 @@ $color-default: colors.bg(colors.$primary) !default; $color-disabled: #999999 !default; +$color-disabled-translucent: #4d1717 !default; $color-selected: colors.bg(colors.$green) !default; $color-caution: colors.bg(colors.$yellow) !default; $color-danger: colors.bg(colors.$red) !default; @@ -16,25 +17,19 @@ $color-transparent-text: rgba(255, 255, 255, 0.5) !default; $border-radius: base.$border-radius !default; $bg-map: colors.$bg-map !default; -@mixin button-color($color, $text-color: null) { +@mixin button-color($color, $text-color: null, $translucent: false) { // Adapt text color to background luminance to ensure high contast - $luminance: luminance($color); + $luminance: if($translucent, 0, luminance($color)); $dynamic-text-color: if($luminance > 0.4, rgba(0, 0, 0, 1), rgba(255, 255, 255, 1)); - transition: - color 100ms, - background-color 100ms; - background-color: $color; + background-color: if($translucent, rgba($color, 0.33), $color); color: if($text-color != null, $text-color, $dynamic-text-color); - - &:focus { - transition: - color 250ms, - background-color 250ms; - } + transition: + color 200ms, + background-color 200ms; &:hover { - background-color: lighten($color, 25%); + background-color: if($translucent, rgba(lighten($color, 25%), 0.5), lighten($color, 25%)); color: $dynamic-text-color; } } @@ -109,42 +104,62 @@ $bg-map: colors.$bg-map !default; white-space: normal; word-wrap: break-word; } + +.Button--modal { + float: right; + z-index: 1; + margin-top: -0.5rem; +} + @each $color-name, $color-value in $bg-map { .Button--color--#{$color-name} { @include button-color($color-value); } + + .Button--color--#{$color-name}--translucent { + @include button-color($color-value, $color-transparent-text, $translucent: true); + } +} + +.Button--color--transparent { + @include button-color(rgba(base.$color-bg, 0), $color-transparent-text); } .Button--color--default { @include button-color($color-default); + + &--translucent { + @include button-color(lighten(base.$color-bg, 10%), $color-transparent-text, $translucent: true); + } } .Button--color--caution { @include button-color($color-caution); + + &--translucent { + @include button-color($color-caution, $color-transparent-text, $translucent: true); + } } .Button--color--danger { @include button-color($color-danger); -} -.Button--color--transparent { - @include button-color(rgba(base.$color-bg, 0), $color-transparent-text); -} - -.Button--color--translucent { - @include button-color(rgba(base.$color-bg, 0.6), $color-transparent-text); + &--translucent { + @include button-color($color-danger, $color-transparent-text, $translucent: true); + } } .Button--disabled { background-color: $color-disabled !important; -} + color: rgba($color-transparent-text, 0.75) !important; -.Button--selected { - @include button-color($color-selected); + &--translucent { + background-color: rgba($color-disabled-translucent, 0.5) !important; + color: $color-transparent-text !important; + } } -.Button--modal { - float: right; - z-index: 1; - margin-top: -0.5rem; +.Button--selected, +.Button--selected--translucent { + @include button-color($color-selected); } diff --git a/tgui/packages/tgui/styles/components/ImageButton.scss b/tgui/packages/tgui/styles/components/ImageButton.scss index 55bb8ca4f680..01e6a2188d9a 100644 --- a/tgui/packages/tgui/styles/components/ImageButton.scss +++ b/tgui/packages/tgui/styles/components/ImageButton.scss @@ -9,215 +9,253 @@ @use '../functions.scss' as *; $color-default: colors.bg(base.$color-bg-section) !default; -$color-disabled: #4d1717 !default; +$color-disabled: #631d1d !default; $color-selected: colors.bg(colors.$green) !default; $bg-map: colors.$bg-map !default; -@mixin button-style($color, $clickable: false) { +@mixin button-style( + $color, + $border-color: rgba(lighten($color, 50%), 0.2), + $border-width: 1px 0 0 0, + $opacity: 0.2, + $hoverable: true, + $transition-duration: 0.2s +) { $luminance: luminance($color); + $text-color: if($luminance > 0.3, rgba(0, 0, 0, 1), rgba(255, 255, 255, 1)); - background-color: rgba($color, 0.15); - border: base.em(1px) solid rgba(lighten($color, 50%), 0.2); - - @if $clickable { - transition: - color 100ms, - background-color 100ms; - - &:focus { - transition: - color 250ms, - background-color 250ms; - } + background-color: rgba($color, $opacity); + color: $text-color; + border: solid $border-color; + border-width: $border-width; + transition: + background-color $transition-duration, + border-color $transition-duration; + @if $hoverable { &:hover { - background-color: rgba(lighten($color, 25%), 0.25); + background-color: rgba(lighten($color, 50%), $opacity); } } } -.ImageButton__image { - line-height: 0; - align-self: center; -} - -.ImageButton__vertical, -.ImageButton--vertical { - display: inline-block; -} - -.ImageButton__horizontal { - display: flex; - user-select: none; - -ms-user-select: none; - width: 100%; - align-items: center; -} - -.ImageButton--horizontal { - display: flex; - margin-bottom: 0.5em; - user-select: none; - -ms-user-select: none; - - &:last-child { - margin-bottom: 0; - } -} - @each $color-name, $color-value in $bg-map { - .ImageButton--color--#{$color-name} { - @include button-style($color-value); + .color__#{$color-name} { + @include button-style($color-value, $border-width: 1px); } - .ImageButton--color--clickable--#{$color-name} { - @include button-style($color-value, true); + .contentColor__#{$color-name} { + @include button-style($color-value, $border-color: lighten($color-value, 25%), $opacity: 1, $hoverable: false); } -} -.ImageButton--color--default { - @include button-style(rgba(lighten($color-default, 100%), 0.2)); + .buttonsContainerColor__#{$color-name} { + @include button-style( + $color-value, + $border-width: 1px 1px 1px 0, + $opacity: 0.33, + $hoverable: false, + $transition-duration: 0 + ); + } } -.ImageButton--color--default--clickable { - @include button-style(rgba(lighten($color-default, 100%), 0.2), true); +.color__default { + @include button-style(lighten($color-default, 85%), $border-width: 1px); } -.ImageButton--disabled { +.disabled { background-color: rgba($color-disabled, 0.25) !important; - border: base.em(1px) solid rgba(lighten($color-disabled, 100%), 0.15); + border-color: rgba($color-disabled, 0.25) !important; } -.ImageButton--selected { - @include button-style($color-selected, true); +.selected { + @include button-style($color-selected, $border-color: rgba($color-selected, 0.25), $border-width: 1px); } -.ImageButton__content__vertical { - height: 1.6em; - padding-top: 2px; - border: 0; - text-align: center; - font-size: 11px; - font-weight: bold; +.contentColor__default { + @include button-style( + lighten($color-default, 80%), + $border-color: lighten($color-default, 100%), + $opacity: 1, + $hoverable: false + ); } -.ImageButton__content__horizontal { - display: block; - text-align: center; - font-size: 12px; - flex-grow: 1; -} - -.ImageButton__content__horizontal--title { - font-weight: bold; - padding: base.em(6px); - padding-bottom: 0; +.contentDisabled { + background-color: $color-disabled !important; + border-top: 1px solid lighten($color-disabled, 25%) !important; } -.ImageButton__content__horizontal--content { - padding: base.em(6px); +.contentSelected { + @include button-style($color-selected, $border-color: lighten($color-selected, 25%), $opacity: 1, $hoverable: false); } -.ImageButton__content__horizontal--divider { - margin: base.em(6px); - margin-bottom: 0; - border-top: Divider.$thickness solid Divider.$color; +.buttonsContainerColor__default { + @include button-style( + lighten($color-default, 85%), + $border-width: 1px 1px 1px 0, + $hoverable: false, + $transition-duration: 0 + ); } -.ImageButton__content--ellipsis { - overflow: hidden; - text-overflow: ellipsis; -} +.ImageButton { + display: inline-table; + position: relative; + text-align: center; + margin: 0.25em; + user-select: none; + -ms-user-select: none; -@mixin content-style($color) { - $luminance: luminance($color); - $text-color: if($luminance > 0.3, rgba(0, 0, 0, 0.9), rgba(255, 255, 255, 1)); + .noAction { + pointer-events: none; + } - color: $text-color; - background-color: darken($color, 2.5%); - border-top: base.em(1px) solid (rgba(lighten($color, 33%), 1)); -} + .container { + display: flex; + flex-direction: column; + border-radius: 0.33em; + } -@each $color-name, $color-value in $bg-map { - .ImageButton__content--color--#{$color-name} { - @include content-style(rgba($color-value, 1)); + .image { + position: relative; + align-self: center; + pointer-events: none; + overflow: hidden; + line-height: 0; + padding: 0.25em; + border-radius: 0.33em; + + * { + image-rendering: pixelated; + -ms-interpolation-mode: nearest-neighbor; // Remove with 516 + } } -} -.ImageButton__content--color--default { - @include content-style(rgba(lighten($color-default, 80%), 1)); -} + .buttonsContainer { + display: flex; + position: absolute; + overflow: hidden; + left: 1px; + bottom: 1.8em; + max-width: 100%; + z-index: 1; + + &.buttonsAltContainer { + overflow: visible; + flex-direction: column; + pointer-events: none; + top: 1px; + bottom: inherit !important; + } -.ImageButton__content--disabled { - background-color: $color-disabled !important; - color: rgba(200, 200, 200, 0.75); - border-top: base.em(1px) solid rgba(lighten($color-disabled, 100%), 0.5); -} + &.buttonsEmpty { + bottom: 1px; + } -.ImageButton__content--selected { - background-color: $color-selected !important; - border-top: base.em(1px) solid rgba(lighten($color-selected, 100%), 0.5); + & > * { + /* I know !important is bad, but here's no other way */ + margin: 0 !important; + padding: 0 0.2em !important; + border-radius: 0 !important; + } + } + + .content { + -ms-user-select: none; + user-select: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0.25em 0.5em; + margin: -1px; + border-radius: 0 0 0.33em 0.33em; + z-index: 2; + } } -.ImageButton__item { +.fluid { display: flex; - justify-content: center; - align-items: center; + flex-direction: row; + position: relative; text-align: center; - width: 100%; - height: 100%; -} + margin: 0 0 0.5em 0; + user-select: none; + -ms-user-select: none; -.ImageButton__item--icon--horizontal { - display: flex; - align-items: center; + &:last-of-type { + margin-bottom: 0; + } - .fa, - .fas, - .far { - margin-left: 0.2em; - margin-right: 0.2em; + .info { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; } -} -@mixin item-style($color) { - $luminance: luminance($color); + .title { + font-weight: bold; + padding: 0.5em; - transition: - color 100ms, - background-color 100ms; - background-color: rgba($color, 0.4); - border: base.em(1px) solid rgba(lighten($color, 50%), 0.2); - border-left: 0; + &.divider { + margin: 0 0.5em; + border-bottom: Divider.$thickness solid Divider.$color; + } + } - &:focus { - transition: - color 250ms, - background-color 250ms; + .contentFluid { + padding: 0.5em; + color: white; } - &:hover { - background-color: rgba(lighten($color, 25%), 0.5); + .container { + flex-direction: row; + flex: 1; + + &.hasButtons { + border-radius: 0.33em 0 0 0.33em; + border-width: 1px 0 1px 1px; + } } -} -@each $color-name, $color-value in $bg-map { - .ImageButton__item--color--#{$color-name} { - @include item-style(rgba($color-value, 1)); + .image { + padding: 0; } -} -.ImageButton__item--color--default { - @include item-style(rgba(lighten($color-default, 100%), 1)); -} + .buttonsContainer { + position: relative; + left: inherit; + bottom: inherit; + border-radius: 0 0.33em 0.33em 0; -.ImageButton__item--disabled { - background-color: rgba($color-disabled, 0.5) !important; - border: base.em(1px) solid rgba(lighten($color-disabled, 100%), 0.15); - border-left: 0; -} + &.buttonsEmpty { + bottom: inherit; + } + + &.buttonsAltContainer { + overflow: hidden; + pointer-events: auto; + top: inherit; + + & > * { + border-top: 1px solid rgba(255, 255, 255, 0.075); -.ImageButton__item--selected { - border-left: 0; - @include item-style($color-selected); + &:first-child { + border-top: 0; + } + } + } + + & > * { + display: inline-flex; + flex-direction: column; + justify-content: center; + text-align: center; + white-space: pre-wrap; + line-height: base.em(14px); + height: 100%; + border-left: 1px solid rgba(255, 255, 255, 0.075); + } + } } diff --git a/tgui/public/tgui-panel.bundle.css b/tgui/public/tgui-panel.bundle.css index 22e52bff0f08..7eafbb083b64 100644 --- a/tgui/public/tgui-panel.bundle.css +++ b/tgui/public/tgui-panel.bundle.css @@ -1 +1 @@ -html,body{box-sizing:border-box;height:100%;margin:0;font-size:12px}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif}*,*:before,*:after{box-sizing:inherit}h1,h2,h3,h4,h5,h6{display:block;margin:0;padding:6px 0;padding:.5rem 0}h1{font-size:18px;font-size:1.5rem}h2{font-size:16px;font-size:1.333rem}h3{font-size:14px;font-size:1.167rem}h4{font-size:12px;font-size:1rem}td,th{vertical-align:baseline;text-align:left}.candystripe:nth-child(odd){background-color:rgba(0,0,0,.25)}.color-black{color:#1a1a1a!important}.color-white{color:#fff!important}.color-red{color:#df3e3e!important}.color-orange{color:#f37f33!important}.color-yellow{color:#fbda21!important}.color-olive{color:#cbe41c!important}.color-green{color:#25ca4c!important}.color-teal{color:#00d6cc!important}.color-blue{color:#2e93de!important}.color-violet{color:#7349cf!important}.color-purple{color:#ad45d0!important}.color-pink{color:#e34da1!important}.color-brown{color:#b97447!important}.color-grey{color:#848484!important}.color-good{color:#68c22d!important}.color-average{color:#f29a29!important}.color-bad{color:#df3e3e!important}.color-label{color:#8b9bb0!important}.color-gold{color:#f3b22f!important}.color-bg-black{background-color:#000!important}.color-bg-white{background-color:#d9d9d9!important}.color-bg-red{background-color:#bd2020!important}.color-bg-orange{background-color:#d95e0c!important}.color-bg-yellow{background-color:#d9b804!important}.color-bg-olive{background-color:#9aad14!important}.color-bg-green{background-color:#1b9638!important}.color-bg-teal{background-color:#009a93!important}.color-bg-blue{background-color:#1c71b1!important}.color-bg-violet{background-color:#552dab!important}.color-bg-purple{background-color:#8b2baa!important}.color-bg-pink{background-color:#cf2082!important}.color-bg-brown{background-color:#8c5836!important}.color-bg-grey{background-color:#646464!important}.color-bg-good{background-color:#4d9121!important}.color-bg-average{background-color:#cd7a0d!important}.color-bg-bad{background-color:#bd2020!important}.color-bg-label{background-color:#657a94!important}.color-bg-gold{background-color:#d6920c!important}.color-border-black{border-color:#1a1a1a!important}.color-border-white{border-color:#fff!important}.color-border-red{border-color:#df3e3e!important}.color-border-orange{border-color:#f37f33!important}.color-border-yellow{border-color:#fbda21!important}.color-border-olive{border-color:#cbe41c!important}.color-border-green{border-color:#25ca4c!important}.color-border-teal{border-color:#00d6cc!important}.color-border-blue{border-color:#2e93de!important}.color-border-violet{border-color:#7349cf!important}.color-border-purple{border-color:#ad45d0!important}.color-border-pink{border-color:#e34da1!important}.color-border-brown{border-color:#b97447!important}.color-border-grey{border-color:#848484!important}.color-border-good{border-color:#68c22d!important}.color-border-average{border-color:#f29a29!important}.color-border-bad{border-color:#df3e3e!important}.color-border-label{border-color:#8b9bb0!important}.color-border-gold{border-color:#f3b22f!important}.debug-layout,.debug-layout *:not(g):not(path){color:rgba(255,255,255,.9)!important;background:rgba(0,0,0,0)!important;outline:1px solid rgba(255,255,255,.5)!important;box-shadow:none!important;filter:none!important}.debug-layout:hover,.debug-layout *:not(g):not(path):hover{outline-color:rgba(255,255,255,.8)!important}.outline-dotted{outline-style:dotted!important}.outline-dashed{outline-style:dashed!important}.outline-solid{outline-style:solid!important}.outline-double{outline-style:double!important}.outline-groove{outline-style:groove!important}.outline-ridge{outline-style:ridge!important}.outline-inset{outline-style:inset!important}.outline-outset{outline-style:outset!important}.outline-color-black{outline:.167rem solid #1a1a1a!important}.outline-color-white{outline:.167rem solid #fff!important}.outline-color-red{outline:.167rem solid #df3e3e!important}.outline-color-orange{outline:.167rem solid #f37f33!important}.outline-color-yellow{outline:.167rem solid #fbda21!important}.outline-color-olive{outline:.167rem solid #cbe41c!important}.outline-color-green{outline:.167rem solid #25ca4c!important}.outline-color-teal{outline:.167rem solid #00d6cc!important}.outline-color-blue{outline:.167rem solid #2e93de!important}.outline-color-violet{outline:.167rem solid #7349cf!important}.outline-color-purple{outline:.167rem solid #ad45d0!important}.outline-color-pink{outline:.167rem solid #e34da1!important}.outline-color-brown{outline:.167rem solid #b97447!important}.outline-color-grey{outline:.167rem solid #848484!important}.outline-color-good{outline:.167rem solid #68c22d!important}.outline-color-average{outline:.167rem solid #f29a29!important}.outline-color-bad{outline:.167rem solid #df3e3e!important}.outline-color-label{outline:.167rem solid #8b9bb0!important}.outline-color-gold{outline:.167rem solid #f3b22f!important}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-baseline{text-align:baseline}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-pre{white-space:pre}.text-bold{font-weight:700}.text-italic{font-style:italic}.text-underline{text-decoration:underline}.BlockQuote{color:#8b9bb0;border-left:.1666666667em solid #8b9bb0;padding-left:.5em;margin-bottom:.5em}.BlockQuote:last-child{margin-bottom:0}.Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.Button:last-child{margin-right:0;margin-bottom:0}.Button .fa,.Button .fas,.Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.Button--hasContent .fa,.Button--hasContent .fas,.Button--hasContent .far{margin-right:.25em}.Button--hasContent.Button--iconRight .fa,.Button--hasContent.Button--iconRight .fas,.Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.Button--fluid{display:block;margin-left:0;margin-right:0}.Button--circular{border-radius:50%}.Button--compact{padding:0 .25em;line-height:1.333em}.Button--multiLine{white-space:normal;word-wrap:break-word}.Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.Button--color--black:focus{transition:color .25s,background-color .25s}.Button--color--black:hover{background-color:#101010;color:#fff}.Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.Button--color--white:focus{transition:color .25s,background-color .25s}.Button--color--white:hover{background-color:#f8f8f8;color:#000}.Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--red:focus{transition:color .25s,background-color .25s}.Button--color--red:hover{background-color:#d93f3f;color:#fff}.Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.Button--color--orange:focus{transition:color .25s,background-color .25s}.Button--color--orange:hover{background-color:#ef7e33;color:#fff}.Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.Button--color--yellow:focus{transition:color .25s,background-color .25s}.Button--color--yellow:hover{background-color:#f5d523;color:#000}.Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.Button--color--olive:focus{transition:color .25s,background-color .25s}.Button--color--olive:hover{background-color:#bdd327;color:#fff}.Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.Button--color--green:focus{transition:color .25s,background-color .25s}.Button--color--green:hover{background-color:#2fb94f;color:#fff}.Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.Button--color--teal:focus{transition:color .25s,background-color .25s}.Button--color--teal:hover{background-color:#10bdb6;color:#fff}.Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.Button--color--blue:focus{transition:color .25s,background-color .25s}.Button--color--blue:hover{background-color:#308fd6;color:#fff}.Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.Button--color--violet:focus{transition:color .25s,background-color .25s}.Button--color--violet:hover{background-color:#7249ca;color:#fff}.Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.Button--color--purple:focus{transition:color .25s,background-color .25s}.Button--color--purple:hover{background-color:#aa46ca;color:#fff}.Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.Button--color--pink:focus{transition:color .25s,background-color .25s}.Button--color--pink:hover{background-color:#e04ca0;color:#fff}.Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.Button--color--brown:focus{transition:color .25s,background-color .25s}.Button--color--brown:hover{background-color:#ae724c;color:#fff}.Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.Button--color--grey:focus{transition:color .25s,background-color .25s}.Button--color--grey:hover{background-color:#818181;color:#fff}.Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.Button--color--good:focus{transition:color .25s,background-color .25s}.Button--color--good:hover{background-color:#67b335;color:#fff}.Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.Button--color--average:focus{transition:color .25s,background-color .25s}.Button--color--average:hover{background-color:#eb972b;color:#fff}.Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--bad:focus{transition:color .25s,background-color .25s}.Button--color--bad:hover{background-color:#d93f3f;color:#fff}.Button--color--label{transition:color .1s,background-color .1s;background-color:#657a94;color:#fff}.Button--color--label:focus{transition:color .25s,background-color .25s}.Button--color--label:hover{background-color:#8a9aae;color:#fff}.Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.Button--color--gold:focus{transition:color .25s,background-color .25s}.Button--color--gold:hover{background-color:#eeaf30;color:#fff}.Button--color--default{transition:color .1s,background-color .1s;background-color:#3e6189;color:#fff}.Button--color--default:focus{transition:color .25s,background-color .25s}.Button--color--default:hover{background-color:#567daa;color:#fff}.Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.Button--color--caution:focus{transition:color .25s,background-color .25s}.Button--color--caution:hover{background-color:#f5d523;color:#000}.Button--color--danger{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--danger:focus{transition:color .25s,background-color .25s}.Button--color--danger:hover{background-color:#d93f3f;color:#fff}.Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(32,32,32,0);color:rgba(255,255,255,.5)}.Button--color--transparent:focus{transition:color .25s,background-color .25s}.Button--color--transparent:hover{background-color:rgba(50,50,50,.81);color:#fff}.Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(32,32,32,.6);color:rgba(255,255,255,.5)}.Button--color--translucent:focus{transition:color .25s,background-color .25s}.Button--color--translucent:hover{background-color:rgba(54,54,54,.925);color:#fff}.Button--disabled{background-color:#999!important}.Button--selected{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.Button--selected:focus{transition:color .25s,background-color .25s}.Button--selected:hover{background-color:#2fb94f;color:#fff}.Button--modal{float:right;z-index:1;margin-top:-.5rem}.ColorBox{display:inline-block;width:1em;height:1em;line-height:1em;text-align:center}.Dimmer{display:flex;justify-content:center;align-items:center;position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.75);z-index:1}.Dropdown{position:relative;align-items:center}.Dropdown__control{display:inline-block;align-items:center;font-family:Verdana,sans-serif;font-size:1em;width:8.3333333333em;line-height:1.3333333333em;-ms-user-select:none;user-select:none}.Dropdown__arrow-button{float:right;padding-left:.35em;width:1.2em;height:1.8333333333em;border-left:.0833333333em solid #000;border-left:.0833333333em solid rgba(0,0,0,.25)}.Dropdown__menu{overflow-y:auto;align-items:center;z-index:5;max-height:16.6666666667em;border-radius:0 0 .1666666667em .1666666667em;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75)}.Dropdown__menu-scroll{overflow-y:scroll}.Dropdown__menuentry{padding:.1666666667em .3333333333em;font-family:Verdana,sans-serif;font-size:1em;line-height:1.4166666667em;transition:background-color .1s ease-out}.Dropdown__menuentry.selected{background-color:rgba(255,255,255,.5)!important;transition:background-color 0ms}.Dropdown__menuentry:hover{background-color:rgba(255,255,255,.2);transition:background-color 0ms}.Dropdown__over{top:auto;bottom:100%}.Dropdown__selected-text{display:inline-block;text-overflow:ellipsis;white-space:nowrap;height:1.4166666667em;width:calc(100% - 1.2em);text-align:left;padding-top:2.5px}.Flex{display:-ms-flexbox;display:flex}.Flex--inline{display:inline-flex}.Flex--iefix{display:block}.Flex--iefix.Flex--inline,.Flex__item--iefix{display:inline-block}.Flex--iefix--column>.Flex__item--iefix{display:block}.Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.Knob__popupValue,.Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Knob__popupValue--right{top:.25rem;right:-50%}.Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.Knob__ringTrackPivot{transform:rotate(135deg)}.Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.Knob__ringFillPivot{transform:rotate(135deg)}.Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.Knob--color--white .Knob__ringFill{stroke:#fff}.Knob--color--red .Knob__ringFill{stroke:#df3e3e}.Knob--color--orange .Knob__ringFill{stroke:#f37f33}.Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.Knob--color--green .Knob__ringFill{stroke:#25ca4c}.Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.Knob--color--blue .Knob__ringFill{stroke:#2e93de}.Knob--color--violet .Knob__ringFill{stroke:#7349cf}.Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.Knob--color--pink .Knob__ringFill{stroke:#e34da1}.Knob--color--brown .Knob__ringFill{stroke:#b97447}.Knob--color--grey .Knob__ringFill{stroke:#848484}.Knob--color--good .Knob__ringFill{stroke:#68c22d}.Knob--color--average .Knob__ringFill{stroke:#f29a29}.Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.LabeledList{display:table;width:100%;width:calc(100% + 1em);border-collapse:collapse;border-spacing:0;margin:-.25em -.5em 0;padding:0}.LabeledList__row{display:table-row}.LabeledList__row:last-child .LabeledList__cell{padding-bottom:0}.LabeledList__cell{display:table-cell;margin:0;padding:.25em .5em;border:0;text-align:left;vertical-align:baseline}.LabeledList__label{width:1%;white-space:nowrap;min-width:5em}.LabeledList__buttons{width:.1%;white-space:nowrap;text-align:right;padding-top:.0833333333em;padding-bottom:0}.LabeledList__breakContents{word-break:break-all;word-wrap:break-word}.Modal{background-color:#202020;max-width:calc(100% - 1rem);padding:1rem;scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#000;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.NoticeBox--color--black{color:#fff;background-color:#000}.NoticeBox--color--white{color:#000;background-color:#b3b3b3}.NoticeBox--color--red{color:#fff;background-color:#701f1f}.NoticeBox--color--orange{color:#fff;background-color:#854114}.NoticeBox--color--yellow{color:#000;background-color:#83710d}.NoticeBox--color--olive{color:#000;background-color:#576015}.NoticeBox--color--green{color:#fff;background-color:#174e24}.NoticeBox--color--teal{color:#fff;background-color:#064845}.NoticeBox--color--blue{color:#fff;background-color:#1b4565}.NoticeBox--color--violet{color:#fff;background-color:#3b2864}.NoticeBox--color--purple{color:#fff;background-color:#542663}.NoticeBox--color--pink{color:#fff;background-color:#802257}.NoticeBox--color--brown{color:#fff;background-color:#4c3729}.NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.NoticeBox--color--average{color:#fff;background-color:#7b4e13}.NoticeBox--color--bad{color:#fff;background-color:#701f1f}.NoticeBox--color--label{color:#fff;background-color:#53565a}.NoticeBox--color--gold{color:#fff;background-color:#825d13}.NoticeBox--type--info{color:#fff;background-color:#235982}.NoticeBox--type--success{color:#fff;background-color:#1e662f}.NoticeBox--type--warning{color:#fff;background-color:#a95219}.NoticeBox--type--danger{color:#fff;background-color:#8f2828}.NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.NumberInput--fluid{display:block}.NumberInput__content{margin-left:.5em}.NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.ProgressBar__fill--animated{transition:background-color .5s,width .5s}.ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.ProgressBar--color--default{border:.0833333333em solid #3e6189}.ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.ProgressBar--color--disabled{border:1px solid #999}.ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.ProgressBar--color--black{border:.0833333333em solid #000!important}.ProgressBar--color--black .ProgressBar__fill{background-color:#000}.ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.ProgressBar--color--grey{border:.0833333333em solid #646464!important}.ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--label{border:.0833333333em solid #657a94!important}.ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.Section{position:relative;margin-bottom:.5em;background-color:#131313;box-sizing:border-box}.Section:last-child{margin-bottom:0}.Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.Section__rest{position:relative}.Section__content{padding:.66em .5em}.Section--fitted>.Section__rest>.Section__content{padding:0}.Section--fill{display:flex;flex-direction:column;height:100%}.Section--fill>.Section__rest{flex-grow:1}.Section--fill>.Section__rest>.Section__content{height:100%}.Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.Section--scrollable{overflow-x:hidden;overflow-y:hidden}.Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.Section .Section:first-child{margin-top:-.5em}.Section .Section .Section__titleText{font-size:1.0833333333em}.Section .Section .Section .Section__titleText{font-size:1em}.Slider:not(.Slider__disabled){cursor:e-resize}.Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Divider--horizontal{margin:.5em 0}.Divider--horizontal:not(.Divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Divider--vertical{height:100%;margin:0 .5em}.Divider--vertical:not(.Divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--fill{height:100%}.Stack--horizontal>.Stack__item{margin-left:.5em}.Stack--horizontal>.Stack__item:first-child{margin-left:0}.Stack--vertical>.Stack__item{margin-top:.5em}.Stack--vertical>.Stack__item:first-child{margin-top:0}.Stack--zebra>.Stack__item:nth-child(2n){background-color:#131313}.Stack--horizontal>.Stack__divider:not(.Stack__divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--vertical>.Stack__divider:not(.Stack__divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Table{display:table;width:100%;border-collapse:collapse;border-spacing:0;margin:0}.Table--collapsing{width:auto}.Table__row{display:table-row}.Table__cell{display:table-cell;padding:0 .25em}.Table__cell:first-child{padding-left:0}.Table__cell:last-child{padding-right:0}.Table__row--header .Table__cell,.Table__cell--header{font-weight:700;padding-bottom:.5em}.Table__cell--collapsing{width:1%;white-space:nowrap}.Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#131313}.Tabs--fill{height:100%}.Section .Tabs{background-color:rgba(0,0,0,0)}.Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.Tabs--horizontal:last-child{margin-bottom:0}.Tabs__Tab{flex-grow:0}.Tabs--fluid .Tabs__Tab{flex-grow:1}.Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(255,255,255,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.Tab:not(.Tab--selected):hover{background-color:rgba(255,255,255,.075);transition:background-color 0}.Tab--selected{background-color:rgba(255,255,255,.125);color:#dfe7f0}.Tab__text{flex-grow:1;margin:0 .5em}.Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #d4dfec}.Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.Tabs--vertical .Tab--selected{border-left:.1666666667em solid #d4dfec}.Tab--selected.Tab--color--black{color:#535353}.Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#1a1a1a}.Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#1a1a1a}.Tab--selected.Tab--color--white{color:#fff}.Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#fff}.Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#fff}.Tab--selected.Tab--color--red{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#df3e3e}.Tab--selected.Tab--color--orange{color:#f69f66}.Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#f37f33}.Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#f37f33}.Tab--selected.Tab--color--yellow{color:#fce358}.Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#fbda21}.Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#fbda21}.Tab--selected.Tab--color--olive{color:#d8eb55}.Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#cbe41c}.Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#cbe41c}.Tab--selected.Tab--color--green{color:#53e074}.Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#25ca4c}.Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#25ca4c}.Tab--selected.Tab--color--teal{color:#21fff5}.Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00d6cc}.Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00d6cc}.Tab--selected.Tab--color--blue{color:#62aee6}.Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#2e93de}.Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#2e93de}.Tab--selected.Tab--color--violet{color:#9676db}.Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#7349cf}.Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#7349cf}.Tab--selected.Tab--color--purple{color:#c274db}.Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#ad45d0}.Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#ad45d0}.Tab--selected.Tab--color--pink{color:#ea79b9}.Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#e34da1}.Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#e34da1}.Tab--selected.Tab--color--brown{color:#ca9775}.Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#b97447}.Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#b97447}.Tab--selected.Tab--color--grey{color:#a3a3a3}.Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#848484}.Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#848484}.Tab--selected.Tab--color--good{color:#8cd95a}.Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#68c22d}.Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#68c22d}.Tab--selected.Tab--color--average{color:#f5b35e}.Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#f29a29}.Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#f29a29}.Tab--selected.Tab--color--bad{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#df3e3e}.Tab--selected.Tab--color--label{color:#a8b4c4}.Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#8b9bb0}.Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#8b9bb0}.Tab--selected.Tab--color--gold{color:#f6c563}.Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#f3b22f}.Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#f3b22f}.Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.Input--fluid{display:block;width:auto}.Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input--monospace .Input__input{font-family:Consolas,monospace}.TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.TextArea--fluid{display:block;width:auto;height:auto}.TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.Tooltip{z-index:2;padding:.5em .75em;pointer-events:none;text-align:left;transition:opacity .15s ease-out;background-color:#000;color:#fff;box-shadow:.1em .1em 1.25em -.1em rgba(0,0,0,.5);border-radius:.16em;max-width:20.8333333333em}.Chat{color:#abc6ec}.Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.Chat__badge:before{content:"x"}.Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.Chat__scrollButton{position:fixed;right:2em;bottom:1em}.Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#131313}.Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.Chat__highlight{color:#000}.Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.ChatMessage{word-wrap:break-word}.ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.Ping{position:relative;padding:.125em .25em;border:.0833333333em solid rgba(140,140,140,.5);border-radius:.25em;width:3.75em;text-align:right}.Ping__indicator{content:"";position:absolute;top:.5em;left:.5em;width:.5em;height:.5em;background-color:#888;border-radius:.25em}.Notifications{position:absolute;top:1em;left:.75em;right:2em}.Notification{color:#fff;background-color:#dc143c;padding:.5em;margin:1em 0}.Notification:first-child{margin-top:0}.Notification:last-child{margin-bottom:0}html,body{scrollbar-color:#363636 #181818}.Layout,.Layout *{scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.Layout__content--flexRow{display:flex;flex-flow:row}.Layout__content--flexColumn{display:flex;flex-flow:column}.Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.Layout__content--noMargin{margin:0}.Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#202020;background-image:linear-gradient(to bottom,#202020,#202020)}.Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.Window__contentPadding:after{height:0}.Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(56,56,56,.25);pointer-events:none}.Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.emoji16x16{vertical-align:middle}a{color:#397ea5}a.popt{text-decoration:none}.popup{position:fixed;top:50%;left:50%;background:#ddd}.popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.popup .close:hover{background:#999}.popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.popup input[type=text]:hover,.popup input[type=text]:active,.popup input[type=text]:focus{border-color:green}.popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.popup input[type=submit]:hover,.popup input[type=submit]:focus,.popup input[type=submit]:active{background:#aaa;cursor:pointer}.changeFont{padding:10px}.changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.changeFont a:hover{background:#ccc}.highlightPopup{padding:10px;text-align:center}.highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.highlightPopup input.highlightColor{background-color:#ff0}.highlightPopup input.highlightTermSubmit{margin-top:5px}.contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.contextMenu a:hover{background-color:#ccc}.filterMessages{padding:5px}.filterMessages div{padding:2px 0}.icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.motd{color:#a4bad6;font-family:Verdana,sans-serif;white-space:normal}.motd h1,.motd h2,.motd h3,.motd h4,.motd h5,.motd h6{color:#a4bad6;text-decoration:underline}.motd a,.motd a:link,.motd a:active,.motd a:hover{color:#a4bad6}.italic,.italics,.emote{font-style:italic}.highlight{background:#ff0}h1,h2,h3,h4,h5,h6{color:#a4bad6;font-family:Georgia,Verdana,sans-serif}em{font-style:normal;font-weight:700}.darkmblue{color:#6685f5}.prefix,.ooc{font-weight:700}.looc{color:#69c;font-weight:700}.adminobserverooc{color:#09c;font-weight:700}.adminooc{color:#b82e00;font-weight:700}.adminobserver{color:#960;font-weight:700}.admin{color:#386aff;font-weight:700}.adminsay{color:#9611d4;font-weight:700}.mentorhelp{color:#07b;font-weight:700}.adminhelp{color:#a00;font-weight:700}.playerreply{color:#80b;font-weight:700}.pmsend{color:#6685f5}.debug{color:#6d2f83}.name,.yell{font-weight:700}.siliconsay{font-family:Courier New,Courier,monospace}.deadsay{color:#e2c1ff}.radio{color:#20b142}.deptradio{color:#939}.comradio{color:#5f5cff}.syndradio{color:#8f4a4b}.dsquadradio{color:#998599}.resteamradio{color:#18bc46}.airadio{color:#ff5ed7}.centradio{color:#2681a5}.secradio{color:#dd3535}.engradio{color:#feac20}.medradio{color:#00b5ad}.sciradio{color:#c68cfa}.supradio{color:#b88646}.srvradio{color:#bbd164}.proradio{color:#b84f92}.admin_channel{color:#03fc9d;font-weight:700}.all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.mentor_channel{color:#775bff;font-weight:700}.mentor_channel_admin{color:#a35cff;font-weight:700}.djradio{color:#960}.binaryradio{color:#1b00fb;font-family:Courier New,Courier,monospace}.mommiradio{color:#6685f5}.alert{color:#d82020}h1.alert,h2.alert{color:#a4bad6}.ghostalert{color:#cc00c6;font-style:italic;font-weight:700}.emote{font-style:italic}.selecteddna{color:#a4bad6;background-color:#001b1b}.attack{color:red}.moderate{color:#c00}.disarm{color:#900}.passive{color:#600}.warning{color:#c51e1e;font-style:italic}.boldwarning{color:#c51e1e;font-style:italic;font-weight:700}.danger{color:#c51e1e;font-weight:700}.userdanger{color:#c51e1e;font-weight:700;font-size:120%}.biggerdanger{color:red;font-weight:700;font-size:150%}.notice{color:#6685f5}.boldnotice{color:#6685f5;font-weight:700}.suicide{color:#ff5050;font-style:italic}.green{color:#03bb39}.pr_announce,.boldannounceic,.boldannounceooc{color:#c51e1e;font-weight:700}.greenannounce{color:#059223;font-weight:700}.alien{color:#c433c4}.noticealien{color:#00c000}.alertalien{color:#00c000;font-weight:700}.terrorspider{color:#cf52fa}.dantalion{color:#8b2c5e}.chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.chaosgood{color:#19e0c0;font-weight:700}.chaosneutral{color:#479ac0;font-weight:700}.chaosbad{color:#9047c0;font-weight:700}.chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.sinister{color:purple;font-weight:700;font-style:italic}.medal{font-weight:700}.blob{color:#006221;font-weight:700;font-style:italic}.confirm{color:#00af3b}.rose{color:#ff5050}.sans{font-family:Comic Sans MS,cursive,sans-serif}.wingdings{font-family:Wingdings,Webdings}.robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.ancient{color:#008b8b;font-style:italic}.newscaster{color:#c00}.mod{color:#735638;font-weight:700}.modooc{color:#184880;font-weight:700}.adminmod{color:#f0aa14;font-weight:700}.tajaran{color:#803b56}.skrell{color:#00ced1}.solcom{color:#8282fb}.com_srus{color:#7c4848}.soghun{color:#228b22}.changeling{color:#00b4de}.vox{color:#a0a}.diona{color:#804000;font-weight:700}.trinary{color:#727272}.kidan{color:#c64c05}.slime{color:#07a}.drask{color:#a3d4eb;font-family:Arial Black}.moth{color:#869b29;font-family:Copperplate}.clown{color:red}.vulpkanin{color:#b97a57}.abductor{color:purple;font-style:italic}.mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.rough{font-family:Trebuchet MS,cursive,sans-serif}.say_quote{font-family:Georgia,Verdana,sans-serif}.cult{color:purple;font-weight:700;font-style:italic}.cultspeech{color:#af0000;font-style:italic}.cultitalic{color:#a60000;font-style:italic}.cultlarge{color:#a60000;font-weight:700;font-size:120%}.narsie{color:#a60000;font-weight:700;font-size:300%}.narsiesmall{color:#a60000;font-weight:700;font-size:200%}.zombie{color:#7c4848}.zombielarge{color:#7c4848;font-weight:700;font-size:120%}.interface{color:#9031c4}.big{font-size:150%}.reallybig{font-size:175%}.greentext{color:#0f0;font-size:150%}.redtext{color:red;font-size:150%}.bold{font-weight:700}.his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.center{text-align:center}.red{color:red}.purple{color:#9031c4}.skeleton{color:#c8c8c8;font-weight:700;font-style:italic}.gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.orange{color:orange}.orangei{color:orange;font-style:italic}.orangeb{color:orange;font-weight:700}.resonate{color:#298f85}.healthscan_oxy{color:#5cc9ff}.revennotice{color:#6685f5}.revenboldnotice{color:#6685f5;font-weight:700}.revenbignotice{color:#6685f5;font-weight:700;font-size:120%}.revenminor{color:#823abb}.revenwarning{color:#760fbb;font-style:italic}.revendanger{color:#760fbb;font-weight:700;font-size:120%}.specialnotice{color:#4a6f82;font-weight:700;font-size:120%}.good{color:green}.average{color:#ff8000}.bad{color:red}.italics,.talkinto{font-style:italic}.whisper{font-style:italic;color:#ccc}.recruit{color:#5c00e6;font-weight:700;font-style:italic}.memo{color:#638500;text-align:center}.memoedit{text-align:center;font-size:75%}.connectionClosed,.fatalError{background:red;color:#fff;padding:5px}.connectionClosed.restored{background:green}.internal.boldnshit{color:#6685f5;font-weight:700}.rebooting{background:#2979af;color:#fff;padding:5px}.rebooting a{color:#fff!important;text-decoration-color:#fff!important}.text-normal{font-weight:400;font-style:normal}.hidden{display:none;visibility:hidden}.colossus{color:#7f282a;font-size:175%}.hierophant{color:#609;font-weight:700;font-style:italic}.hierophant_warning{color:#609;font-style:italic}.emoji{max-height:16px;max-width:16px}.adminticket{color:#3daf21;font-weight:700}.adminticketalt{color:#ccb847;font-weight:700}span.body .codephrases{color:#55f}span.body .coderesponses{color:#f33}.announcement h1,.announcement h2{color:#a4bad6;margin:8pt 0;line-height:1.2}.announcement p{color:#d82020;line-height:1.3}.announcement.minor h1{font-size:180%}.announcement.minor h2{font-size:170%}.announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.bolditalics{font-style:italic;font-weight:700}.boxed_message{background:#1b1c1e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.boxed_message.left_align_text{text-align:left}.boxed_message.red_border{background:#1e1b1b;border-color:#a00}.boxed_message.green_border{background:#1b1e1c;border-color:#0f0}.boxed_message.purple_border{background:#1d1c1f;border-color:#8000ff}.boxed_message.notice_border{background:#1b1c1e;border-color:#6685f5}.boxed_message.thick_border{border-width:thick}.oxygen{color:#449dff}.nitrogen{color:#f94541}.carbon_dioxide{color:#ccc}.plasma{color:#eb6b00}.sleeping_agent{color:#f28b89}.agent_b{color:teal}.theme-light .color-black{color:#000!important}.theme-light .color-white{color:#e6e6e6!important}.theme-light .color-red{color:#c82121!important}.theme-light .color-orange{color:#e6630d!important}.theme-light .color-yellow{color:#e5c304!important}.theme-light .color-olive{color:#a3b816!important}.theme-light .color-green{color:#1d9f3b!important}.theme-light .color-teal{color:#00a39c!important}.theme-light .color-blue{color:#1e78bb!important}.theme-light .color-violet{color:#5a30b5!important}.theme-light .color-purple{color:#932eb4!important}.theme-light .color-pink{color:#db228a!important}.theme-light .color-brown{color:#955d39!important}.theme-light .color-grey{color:#e6e6e6!important}.theme-light .color-good{color:#529923!important}.theme-light .color-average{color:#da810e!important}.theme-light .color-bad{color:#c82121!important}.theme-light .color-label{color:#353535!important}.theme-light .color-gold{color:#e39b0d!important}.theme-light .color-bg-black{background-color:#000!important}.theme-light .color-bg-white{background-color:#bfbfbf!important}.theme-light .color-bg-red{background-color:#a61c1c!important}.theme-light .color-bg-orange{background-color:#c0530b!important}.theme-light .color-bg-yellow{background-color:#bfa303!important}.theme-light .color-bg-olive{background-color:#889912!important}.theme-light .color-bg-green{background-color:#188532!important}.theme-light .color-bg-teal{background-color:#008882!important}.theme-light .color-bg-blue{background-color:#19649c!important}.theme-light .color-bg-violet{background-color:#4b2897!important}.theme-light .color-bg-purple{background-color:#7a2696!important}.theme-light .color-bg-pink{background-color:#b61d73!important}.theme-light .color-bg-brown{background-color:#7c4d2f!important}.theme-light .color-bg-grey{background-color:#bfbfbf!important}.theme-light .color-bg-good{background-color:#44801d!important}.theme-light .color-bg-average{background-color:#b56b0b!important}.theme-light .color-bg-bad{background-color:#a61c1c!important}.theme-light .color-bg-label{background-color:#2c2c2c!important}.theme-light .color-bg-gold{background-color:#bd810b!important}.theme-light .color-border-black{border-color:#000!important}.theme-light .color-border-white{border-color:#e6e6e6!important}.theme-light .color-border-red{border-color:#c82121!important}.theme-light .color-border-orange{border-color:#e6630d!important}.theme-light .color-border-yellow{border-color:#e5c304!important}.theme-light .color-border-olive{border-color:#a3b816!important}.theme-light .color-border-green{border-color:#1d9f3b!important}.theme-light .color-border-teal{border-color:#00a39c!important}.theme-light .color-border-blue{border-color:#1e78bb!important}.theme-light .color-border-violet{border-color:#5a30b5!important}.theme-light .color-border-purple{border-color:#932eb4!important}.theme-light .color-border-pink{border-color:#db228a!important}.theme-light .color-border-brown{border-color:#955d39!important}.theme-light .color-border-grey{border-color:#e6e6e6!important}.theme-light .color-border-good{border-color:#529923!important}.theme-light .color-border-average{border-color:#da810e!important}.theme-light .color-border-bad{border-color:#c82121!important}.theme-light .color-border-label{border-color:#353535!important}.theme-light .color-border-gold{border-color:#e39b0d!important}.theme-light .Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#fff}.theme-light .Tabs--fill{height:100%}.theme-light .Section .Tabs{background-color:rgba(0,0,0,0)}.theme-light .Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.theme-light .Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.theme-light .Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.theme-light .Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.theme-light .Tabs--horizontal:last-child{margin-bottom:0}.theme-light .Tabs__Tab{flex-grow:0}.theme-light .Tabs--fluid .Tabs__Tab{flex-grow:1}.theme-light .Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(0,0,0,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.theme-light .Tab:not(.Tab--selected):hover{background-color:rgba(0,0,0,.075);transition:background-color 0}.theme-light .Tab--selected{background-color:rgba(0,0,0,.125);color:#404040}.theme-light .Tab__text{flex-grow:1;margin:0 .5em}.theme-light .Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.theme-light .Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.theme-light .Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.theme-light .Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #000}.theme-light .Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.theme-light .Tabs--vertical .Tab--selected{border-left:.1666666667em solid #000}.theme-light .Tab--selected.Tab--color--black{color:#404040}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#000}.theme-light .Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#000}.theme-light .Tab--selected.Tab--color--white{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--red{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--orange{color:#f48942}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#e6630d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#e6630d}.theme-light .Tab--selected.Tab--color--yellow{color:#fcdd33}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#e5c304}.theme-light .Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#e5c304}.theme-light .Tab--selected.Tab--color--olive{color:#d0e732}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#a3b816}.theme-light .Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#a3b816}.theme-light .Tab--selected.Tab--color--green{color:#33da5a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#1d9f3b}.theme-light .Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#1d9f3b}.theme-light .Tab--selected.Tab--color--teal{color:#00faef}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00a39c}.theme-light .Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00a39c}.theme-light .Tab--selected.Tab--color--blue{color:#419ce1}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#1e78bb}.theme-light .Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#1e78bb}.theme-light .Tab--selected.Tab--color--violet{color:#7f58d3}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#5a30b5}.theme-light .Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#5a30b5}.theme-light .Tab--selected.Tab--color--purple{color:#b455d4}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#932eb4}.theme-light .Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#932eb4}.theme-light .Tab--selected.Tab--color--pink{color:#e558a7}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#db228a}.theme-light .Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#db228a}.theme-light .Tab--selected.Tab--color--brown{color:#c0825a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#955d39}.theme-light .Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#955d39}.theme-light .Tab--selected.Tab--color--grey{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--good{color:#77d23b}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#529923}.theme-light .Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#529923}.theme-light .Tab--selected.Tab--color--average{color:#f3a23a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#da810e}.theme-light .Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#da810e}.theme-light .Tab--selected.Tab--color--bad{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--label{color:#686868}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#353535}.theme-light .Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#353535}.theme-light .Tab--selected.Tab--color--gold{color:#f4b73f}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#e39b0d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#e39b0d}.theme-light .Section{position:relative;margin-bottom:.5em;background-color:#fff;box-sizing:border-box}.theme-light .Section:last-child{margin-bottom:0}.theme-light .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #fff}.theme-light .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#000}.theme-light .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-light .Section__rest{position:relative}.theme-light .Section__content{padding:.66em .5em}.theme-light .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-light .Section--fill{display:flex;flex-direction:column;height:100%}.theme-light .Section--fill>.Section__rest{flex-grow:1}.theme-light .Section--fill>.Section__rest>.Section__content{height:100%}.theme-light .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-light .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-light .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-light .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-light .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-light .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-light .Section .Section:first-child{margin-top:-.5em}.theme-light .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-light .Section .Section .Section .Section__titleText{font-size:1em}.theme-light .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-light .Button:last-child{margin-right:0;margin-bottom:0}.theme-light .Button .fa,.theme-light .Button .fas,.theme-light .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-light .Button--hasContent .fa,.theme-light .Button--hasContent .fas,.theme-light .Button--hasContent .far{margin-right:.25em}.theme-light .Button--hasContent.Button--iconRight .fa,.theme-light .Button--hasContent.Button--iconRight .fas,.theme-light .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-light .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-light .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-light .Button--circular{border-radius:50%}.theme-light .Button--compact{padding:0 .25em;line-height:1.333em}.theme-light .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-light .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-light .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--black:hover{background-color:#101010;color:#fff}.theme-light .Button--color--white{transition:color .1s,background-color .1s;background-color:#bfbfbf;color:#000}.theme-light .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--white:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--red{transition:color .1s,background-color .1s;background-color:#a61c1c;color:#fff}.theme-light .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--red:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--orange{transition:color .1s,background-color .1s;background-color:#c0530b;color:#fff}.theme-light .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--orange:hover{background-color:#e76d1d;color:#fff}.theme-light .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#bfa303;color:#fff}.theme-light .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--yellow:hover{background-color:#e7c714;color:#fff}.theme-light .Button--color--olive{transition:color .1s,background-color .1s;background-color:#889912;color:#fff}.theme-light .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--olive:hover{background-color:#a9bc25;color:#fff}.theme-light .Button--color--green{transition:color .1s,background-color .1s;background-color:#188532;color:#fff}.theme-light .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--green:hover{background-color:#2ba648;color:#fff}.theme-light .Button--color--teal{transition:color .1s,background-color .1s;background-color:#008882;color:#fff}.theme-light .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--teal:hover{background-color:#10a9a2;color:#fff}.theme-light .Button--color--blue{transition:color .1s,background-color .1s;background-color:#19649c;color:#fff}.theme-light .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--blue:hover{background-color:#2c81c0;color:#fff}.theme-light .Button--color--violet{transition:color .1s,background-color .1s;background-color:#4b2897;color:#fff}.theme-light .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--violet:hover{background-color:#653db9;color:#fff}.theme-light .Button--color--purple{transition:color .1s,background-color .1s;background-color:#7a2696;color:#fff}.theme-light .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--purple:hover{background-color:#9a3bb9;color:#fff}.theme-light .Button--color--pink{transition:color .1s,background-color .1s;background-color:#b61d73;color:#fff}.theme-light .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--pink:hover{background-color:#d93591;color:#fff}.theme-light .Button--color--brown{transition:color .1s,background-color .1s;background-color:#7c4d2f;color:#fff}.theme-light .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--brown:hover{background-color:#9c6745;color:#fff}.theme-light .Button--color--grey{transition:color .1s,background-color .1s;background-color:#bfbfbf;color:#000}.theme-light .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--grey:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--good{transition:color .1s,background-color .1s;background-color:#44801d;color:#fff}.theme-light .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--good:hover{background-color:#5d9f31;color:#fff}.theme-light .Button--color--average{transition:color .1s,background-color .1s;background-color:#b56b0b;color:#fff}.theme-light .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--average:hover{background-color:#dc891d;color:#fff}.theme-light .Button--color--bad{transition:color .1s,background-color .1s;background-color:#a61c1c;color:#fff}.theme-light .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--bad:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--label{transition:color .1s,background-color .1s;background-color:#2c2c2c;color:#fff}.theme-light .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--label:hover{background-color:#424242;color:#fff}.theme-light .Button--color--gold{transition:color .1s,background-color .1s;background-color:#bd810b;color:#fff}.theme-light .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--gold:hover{background-color:#e5a11c;color:#fff}.theme-light .Button--color--default{transition:color .1s,background-color .1s;background-color:#bbb;color:#000}.theme-light .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--default:hover{background-color:#e3e3e3;color:#000}.theme-light .Button--color--caution{transition:color .1s,background-color .1s;background-color:#be6209;color:#fff}.theme-light .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-light .Button--color--danger{transition:color .1s,background-color .1s;background-color:#9a9d00;color:#fff}.theme-light .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-light .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(238,238,238,0);color:rgba(0,0,0,.5)}.theme-light .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--transparent:hover{background-color:rgba(255,255,255,.81);color:#000}.theme-light .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(238,238,238,.6);color:rgba(0,0,0,.5)}.theme-light .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--translucent:hover{background-color:rgba(253,253,253,.925);color:#000}.theme-light .Button--disabled{background-color:#363636!important}.theme-light .Button--selected{transition:color .1s,background-color .1s;background-color:#0668b8;color:#fff}.theme-light .Button--selected:focus{transition:color .25s,background-color .25s}.theme-light .Button--selected:hover{background-color:#1785df;color:#fff}.theme-light .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-light .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#353535;background-color:#e6e6e6;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-light .NumberInput--fluid{display:block}.theme-light .NumberInput__content{margin-left:.5em}.theme-light .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-light .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #353535;background-color:#353535}.theme-light .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#e6e6e6;color:#000;text-align:right}.theme-light .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#000;background-color:#e6e6e6;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-light .Input--disabled{color:#777;border-color:#000;border-color:rgba(0,0,0,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-light .Input--fluid{display:block;width:auto}.theme-light .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-light .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#000;color:inherit}.theme-light .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-light .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-light .TextArea{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;background-color:#e6e6e6;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-light .TextArea--fluid{display:block;width:auto;height:auto}.theme-light .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-light .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-light .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-light .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-light .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-light .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-light .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-light .Knob__popupValue,.theme-light .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .Knob__popupValue--right{top:.25rem;right:-50%}.theme-light .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-light .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-light .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-light .Knob__ringFillPivot{transform:rotate(135deg)}.theme-light .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-light .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-light .Knob--color--black .Knob__ringFill{stroke:#000}.theme-light .Knob--color--white .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--red .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--orange .Knob__ringFill{stroke:#e6630d}.theme-light .Knob--color--yellow .Knob__ringFill{stroke:#e5c304}.theme-light .Knob--color--olive .Knob__ringFill{stroke:#a3b816}.theme-light .Knob--color--green .Knob__ringFill{stroke:#1d9f3b}.theme-light .Knob--color--teal .Knob__ringFill{stroke:#00a39c}.theme-light .Knob--color--blue .Knob__ringFill{stroke:#1e78bb}.theme-light .Knob--color--violet .Knob__ringFill{stroke:#5a30b5}.theme-light .Knob--color--purple .Knob__ringFill{stroke:#932eb4}.theme-light .Knob--color--pink .Knob__ringFill{stroke:#db228a}.theme-light .Knob--color--brown .Knob__ringFill{stroke:#955d39}.theme-light .Knob--color--grey .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--good .Knob__ringFill{stroke:#529923}.theme-light .Knob--color--average .Knob__ringFill{stroke:#da810e}.theme-light .Knob--color--bad .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--label .Knob__ringFill{stroke:#353535}.theme-light .Knob--color--gold .Knob__ringFill{stroke:#e39b0d}.theme-light .Slider:not(.Slider__disabled){cursor:e-resize}.theme-light .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-light .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #000}.theme-light .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #000}.theme-light .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-light .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-light .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-light .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-light .ProgressBar--color--default{border:.0833333333em solid #bfbfbf}.theme-light .ProgressBar--color--default .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--disabled{border:1px solid #999}.theme-light .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-light .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-light .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-light .ProgressBar--color--white{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--white .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--red{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--red .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--orange{border:.0833333333em solid #c0530b!important}.theme-light .ProgressBar--color--orange .ProgressBar__fill{background-color:#c0530b}.theme-light .ProgressBar--color--yellow{border:.0833333333em solid #bfa303!important}.theme-light .ProgressBar--color--yellow .ProgressBar__fill{background-color:#bfa303}.theme-light .ProgressBar--color--olive{border:.0833333333em solid #889912!important}.theme-light .ProgressBar--color--olive .ProgressBar__fill{background-color:#889912}.theme-light .ProgressBar--color--green{border:.0833333333em solid #188532!important}.theme-light .ProgressBar--color--green .ProgressBar__fill{background-color:#188532}.theme-light .ProgressBar--color--teal{border:.0833333333em solid #008882!important}.theme-light .ProgressBar--color--teal .ProgressBar__fill{background-color:#008882}.theme-light .ProgressBar--color--blue{border:.0833333333em solid #19649c!important}.theme-light .ProgressBar--color--blue .ProgressBar__fill{background-color:#19649c}.theme-light .ProgressBar--color--violet{border:.0833333333em solid #4b2897!important}.theme-light .ProgressBar--color--violet .ProgressBar__fill{background-color:#4b2897}.theme-light .ProgressBar--color--purple{border:.0833333333em solid #7a2696!important}.theme-light .ProgressBar--color--purple .ProgressBar__fill{background-color:#7a2696}.theme-light .ProgressBar--color--pink{border:.0833333333em solid #b61d73!important}.theme-light .ProgressBar--color--pink .ProgressBar__fill{background-color:#b61d73}.theme-light .ProgressBar--color--brown{border:.0833333333em solid #7c4d2f!important}.theme-light .ProgressBar--color--brown .ProgressBar__fill{background-color:#7c4d2f}.theme-light .ProgressBar--color--grey{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--grey .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--good{border:.0833333333em solid #44801d!important}.theme-light .ProgressBar--color--good .ProgressBar__fill{background-color:#44801d}.theme-light .ProgressBar--color--average{border:.0833333333em solid #b56b0b!important}.theme-light .ProgressBar--color--average .ProgressBar__fill{background-color:#b56b0b}.theme-light .ProgressBar--color--bad{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--bad .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--label{border:.0833333333em solid #2c2c2c!important}.theme-light .ProgressBar--color--label .ProgressBar__fill{background-color:#2c2c2c}.theme-light .ProgressBar--color--gold{border:.0833333333em solid #bd810b!important}.theme-light .ProgressBar--color--gold .ProgressBar__fill{background-color:#bd810b}.theme-light .Chat{color:#000}.theme-light .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-light .Chat__badge:before{content:"x"}.theme-light .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-light .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-light .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-light .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#fff}.theme-light .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-light .Chat__highlight{color:#000}.theme-light .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-light .ChatMessage{word-wrap:break-word}.theme-light .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-light .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-light html,.theme-light body{scrollbar-color:#a7a7a7 #f2f2f2}.theme-light .Layout,.theme-light .Layout *{scrollbar-base-color:#f2f2f2;scrollbar-face-color:#d6d6d6;scrollbar-3dlight-color:#eee;scrollbar-highlight-color:#eee;scrollbar-track-color:#f2f2f2;scrollbar-arrow-color:#777;scrollbar-shadow-color:#d6d6d6}.theme-light .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-light .Layout__content--flexRow{display:flex;flex-flow:row}.theme-light .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-light .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-light .Layout__content--noMargin{margin:0}.theme-light .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#000;background-color:#eee;background-image:linear-gradient(to bottom,#eee,#eee)}.theme-light .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-light .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-light .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-light .Window__contentPadding:after{height:0}.theme-light .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-light .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(252,252,252,.25);pointer-events:none}.theme-light .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-light .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-light .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-light .TitleBar{background-color:#eee;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-light .TitleBar__clickable{color:rgba(0,0,0,.5);background-color:#eee;transition:color .25s,background-color .25s}.theme-light .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-light .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(0,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-light .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-light .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-light .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-light .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-light html,.theme-light body{padding:0;margin:0;height:100%;color:#000}.theme-light body{background:#fff;font-family:Verdana,sans-serif;font-size:13px;line-height:1.2;overflow-x:hidden;overflow-y:scroll;word-wrap:break-word}.theme-light img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}.theme-light img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.theme-light a{color:#00f}.theme-light a.popt{text-decoration:none}.theme-light .popup{position:fixed;top:50%;left:50%;background:#ddd}.theme-light .popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.theme-light .popup .close:hover{background:#999}.theme-light .popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.theme-light .popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.theme-light .popup input[type=text]:hover,.theme-light .popup input[type=text]:active,.theme-light .popup input[type=text]:focus{border-color:green}.theme-light .popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.theme-light .popup input[type=submit]:hover,.theme-light .popup input[type=submit]:focus,.theme-light .popup input[type=submit]:active{background:#aaa;cursor:pointer}.theme-light .changeFont{padding:10px}.theme-light .changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.theme-light .changeFont a:hover{background:#ccc}.theme-light .highlightPopup{padding:10px;text-align:center}.theme-light .highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.theme-light .highlightPopup input.highlightColor{background-color:#ff0}.theme-light .highlightPopup input.highlightTermSubmit{margin-top:5px}.theme-light .contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.theme-light .contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.theme-light .contextMenu a:hover{background-color:#ccc}.theme-light .filterMessages{padding:5px}.theme-light .filterMessages div{padding:2px 0}.theme-light .icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.theme-light .motd{color:#638500;font-family:Verdana,sans-serif;white-space:normal}.theme-light .motd h1,.theme-light .motd h2,.theme-light .motd h3,.theme-light .motd h4,.theme-light .motd h5,.theme-light .motd h6{color:#638500;text-decoration:underline}.theme-light .motd a,.theme-light .motd a:link,.theme-light .motd a:active,.theme-light .motd a:hover{color:#638500}.theme-light .italic,.theme-light .italics,.theme-light .emote{font-style:italic}.theme-light .highlight{background:#ff0}.theme-light h1,.theme-light h2,.theme-light h3,.theme-light h4,.theme-light h5,.theme-light h6{color:#00f;font-family:Georgia,Verdana,sans-serif}.theme-light em{font-style:normal;font-weight:700}.theme-light .darkmblue{color:#00f}.theme-light .prefix,.theme-light .ooc{font-weight:700}.theme-light .looc{color:#69c;font-weight:700}.theme-light .adminobserverooc{color:#09c;font-weight:700}.theme-light .adminooc{color:#b82e00;font-weight:700}.theme-light .adminobserver{color:#960;font-weight:700}.theme-light .admin{color:#386aff;font-weight:700}.theme-light .adminsay{color:#9611d4;font-weight:700}.theme-light .mentorhelp{color:#07b;font-weight:700}.theme-light .adminhelp{color:#a00;font-weight:700}.theme-light .playerreply{color:#80b;font-weight:700}.theme-light .pmsend{color:#00f}.theme-light .debug{color:#6d2f83}.theme-light .name,.theme-light .yell{font-weight:700}.theme-light .siliconsay{font-family:Courier New,Courier,monospace}.theme-light .deadsay{color:#5c00e6}.theme-light .radio{color:#408010}.theme-light .deptradio{color:#939}.theme-light .comradio{color:#204090}.theme-light .syndradio{color:#6d3f40}.theme-light .dsquadradio{color:#686868}.theme-light .resteamradio{color:#18bc46}.theme-light .airadio{color:#f0f}.theme-light .centradio{color:#5c5c7c}.theme-light .secradio{color:#a30000}.theme-light .engradio{color:#a66300}.theme-light .medradio{color:#009190}.theme-light .sciradio{color:#939}.theme-light .supradio{color:#7f6539}.theme-light .srvradio{color:#80a000}.theme-light .proradio{color:#e3027a}.theme-light .admin_channel{color:#9a04d1;font-weight:700}.theme-light .all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.theme-light .mentor_channel{color:#775bff;font-weight:700}.theme-light .mentor_channel_admin{color:#a35cff;font-weight:700}.theme-light .djradio{color:#630}.theme-light .binaryradio{color:#0b0050;font-family:Courier New,Courier,monospace}.theme-light .mommiradio{color:navy}.theme-light .alert{color:red}.theme-light h1.alert,.theme-light h2.alert{color:#000}.theme-light .ghostalert{color:#5c00e6;font-style:italic;font-weight:700}.theme-light .emote{font-style:italic}.theme-light .selecteddna{color:#fff;background-color:#001b1b}.theme-light .attack{color:red}.theme-light .moderate{color:#c00}.theme-light .disarm{color:#900}.theme-light .passive{color:#600}.theme-light .warning{color:red;font-style:italic}.theme-light .boldwarning{color:red;font-style:italic;font-weight:700}.theme-light .danger{color:red;font-weight:700}.theme-light .userdanger{color:red;font-weight:700;font-size:120%}.theme-light .biggerdanger{color:red;font-weight:700;font-size:150%}.theme-light .notice{color:#009}.theme-light .boldnotice{color:#009;font-weight:700}.theme-light .suicide{color:#ff5050;font-style:italic}.theme-light .green{color:#03bb39}.theme-light .pr_announce{color:#228b22;font-weight:700}.theme-light .boldannounceic,.theme-light .boldannounceooc{color:red;font-weight:700}.theme-light .greenannounce{color:#0f0;font-weight:700}.theme-light .alien{color:#543354}.theme-light .noticealien{color:#00c000}.theme-light .alertalien{color:#00c000;font-weight:700}.theme-light .terrorspider{color:#320e32}.theme-light .dantalion{color:#6a2148}.theme-light .chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.theme-light .chaosgood{color:#19e0c0;font-weight:700}.theme-light .chaosneutral{color:#479ac0;font-weight:700}.theme-light .chaosbad{color:#9047c0;font-weight:700}.theme-light .chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.theme-light .sinister{color:purple;font-weight:700;font-style:italic}.theme-light .blob{color:#006221;font-weight:700;font-style:italic}.theme-light .confirm{color:#00af3b}.theme-light .rose{color:#ff5050}.theme-light .sans{font-family:Comic Sans MS,cursive,sans-serif}.theme-light .wingdings{font-family:Wingdings,Webdings}.theme-light .robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.theme-light .ancient{color:#008b8b;font-style:italic}.theme-light .newscaster{color:maroon}.theme-light .mod{color:#735638;font-weight:700}.theme-light .modooc{color:#184880;font-weight:700}.theme-light .adminmod{color:#402a14;font-weight:700}.theme-light .tajaran{color:#803b56}.theme-light .skrell{color:#00ced1}.theme-light .solcom{color:#22228b}.theme-light .com_srus{color:#7c4848}.theme-light .soghun{color:#228b22}.theme-light .changeling{color:purple}.theme-light .vox{color:#a0a}.theme-light .diona{color:#804000;font-weight:700}.theme-light .trinary{color:#727272}.theme-light .kidan{color:#664205}.theme-light .slime{color:#07a}.theme-light .drask{color:#a3d4eb;font-family:Arial Black}.theme-light .moth{color:#869b29;font-family:Copperplate}.theme-light .clown{color:red}.theme-light .vulpkanin{color:#b97a57}.theme-light .abductor{color:purple;font-style:italic}.theme-light .mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.theme-light .rough{font-family:Trebuchet MS,cursive,sans-serif}.theme-light .say_quote{font-family:Georgia,Verdana,sans-serif}.theme-light .cult{color:purple;font-weight:700;font-style:italic}.theme-light .cultspeech{color:#7f0000;font-style:italic}.theme-light .cultitalic{color:#960000;font-style:italic}.theme-light .cultlarge{color:#960000;font-weight:700;font-size:120%}.theme-light .narsie{color:#960000;font-weight:700;font-size:300%}.theme-light .narsiesmall{color:#960000;font-weight:700;font-size:200%}.theme-light .zombie{color:#7c4848}.theme-light .zombielarge{color:#7c4848;font-weight:700;font-size:120%}.theme-light .interface{color:#303}.theme-light .big{font-size:150%}.theme-light .reallybig{font-size:175%}.theme-light .greentext{color:#0f0;font-size:150%}.theme-light .redtext{color:red;font-size:150%}.theme-light .bold{font-weight:700}.theme-light .his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.theme-light .center{text-align:center}.theme-light .red{color:red}.theme-light .purple{color:#5e2d79}.theme-light .skeleton{color:#585858;font-weight:700;font-style:italic}.theme-light .gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.theme-light .orange{color:orange}.theme-light .orangei{color:orange;font-style:italic}.theme-light .orangeb{color:orange;font-weight:700}.theme-light .resonate{color:#298f85}.theme-light .healthscan_oxy{color:#0074bd}.theme-light .revennotice{color:#1d2953}.theme-light .revenboldnotice{color:#1d2953;font-weight:700}.theme-light .revenbignotice{color:#1d2953;font-weight:700;font-size:120%}.theme-light .revenminor{color:#823abb}.theme-light .revenwarning{color:#760fbb;font-style:italic}.theme-light .revendanger{color:#760fbb;font-weight:700;font-size:120%}.theme-light .specialnoticebold{color:#36525e;font-weight:700;font-size:120%}.theme-light .specialnotice{color:#36525e;font-size:120%}.theme-light .medal{font-weight:700}.theme-light .good{color:green}.theme-light .average{color:#ff8000}.theme-light .bad{color:red}.theme-light .italics,.theme-light .talkinto{font-style:italic}.theme-light .whisper{font-style:italic;color:#333}.theme-light .recruit{color:#5c00e6;font-weight:700;font-style:italic}.theme-light .memo{color:#638500;text-align:center}.theme-light .memoedit{text-align:center;font-size:75%}.theme-light .connectionClosed,.theme-light .fatalError{background:red;color:#fff;padding:5px}.theme-light .connectionClosed.restored{background:green}.theme-light .internal.boldnshit{color:#00f;font-weight:700}.theme-light .rebooting{background:#2979af;color:#fff;padding:5px}.theme-light .rebooting a{color:#fff!important;text-decoration-color:#fff!important}.theme-light .text-normal{font-weight:400;font-style:normal}.theme-light .hidden{display:none;visibility:hidden}.theme-light .colossus{color:#7f282a;font-size:175%}.theme-light .hierophant{color:#609;font-weight:700;font-style:italic}.theme-light .hierophant_warning{color:#609;font-style:italic}.theme-light .emoji{max-height:16px;max-width:16px}.theme-light .adminticket{color:#3e7336;font-weight:700}.theme-light .adminticketalt{color:#014c8a;font-weight:700}.theme-light span.body .codephrases{color:#00f}.theme-light span.body .coderesponses{color:red}.theme-light .announcement h1,.theme-light .announcement h2{color:#000;margin:8pt 0;line-height:1.2}.theme-light .announcement p{color:#d82020;line-height:1.3}.theme-light .announcement.minor h1{font-size:180%}.theme-light .announcement.minor h2{font-size:170%}.theme-light .announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.theme-light .bolditalics{font-style:italic;font-weight:700}.theme-light .boxed_message{background:#f7fcff;border:1px solid #111a26;margin:.5em;padding:.5em .75em;text-align:center}.theme-light .boxed_message.left_align_text{text-align:left}.theme-light .boxed_message.red_border{background:#fff7f7;border-color:#a00}.theme-light .boxed_message.green_border{background:#f7fff7;border-color:#0f0}.theme-light .boxed_message.purple_border{background:#fdf7ff;border-color:#a0f}.theme-light .boxed_message.notice_border{background:#f7fdff;border-color:#0000bf}.theme-light .boxed_message.thick_border{border-width:thick}.theme-light .oxygen{color:#006adb}.theme-light .nitrogen{color:#d00a06}.theme-light .carbon_dioxide{color:#1f1f1f}.theme-light .plasma{color:#853c00}.theme-light .sleeping_agent{color:#e82f2c}.theme-light .agent_b{color:#004d4d}.theme-ntos .color-black{color:#1a1a1a!important}.theme-ntos .color-white{color:#fff!important}.theme-ntos .color-red{color:#df3e3e!important}.theme-ntos .color-orange{color:#f37f33!important}.theme-ntos .color-yellow{color:#fbda21!important}.theme-ntos .color-olive{color:#cbe41c!important}.theme-ntos .color-green{color:#25ca4c!important}.theme-ntos .color-teal{color:#00d6cc!important}.theme-ntos .color-blue{color:#2e93de!important}.theme-ntos .color-violet{color:#7349cf!important}.theme-ntos .color-purple{color:#ad45d0!important}.theme-ntos .color-pink{color:#e34da1!important}.theme-ntos .color-brown{color:#b97447!important}.theme-ntos .color-grey{color:#848484!important}.theme-ntos .color-good{color:#68c22d!important}.theme-ntos .color-average{color:#f29a29!important}.theme-ntos .color-bad{color:#df3e3e!important}.theme-ntos .color-label{color:#8b9bb0!important}.theme-ntos .color-gold{color:#f3b22f!important}.theme-ntos .color-bg-black{background-color:#000!important}.theme-ntos .color-bg-white{background-color:#d9d9d9!important}.theme-ntos .color-bg-red{background-color:#bd2020!important}.theme-ntos .color-bg-orange{background-color:#d95e0c!important}.theme-ntos .color-bg-yellow{background-color:#d9b804!important}.theme-ntos .color-bg-olive{background-color:#9aad14!important}.theme-ntos .color-bg-green{background-color:#1b9638!important}.theme-ntos .color-bg-teal{background-color:#009a93!important}.theme-ntos .color-bg-blue{background-color:#1c71b1!important}.theme-ntos .color-bg-violet{background-color:#552dab!important}.theme-ntos .color-bg-purple{background-color:#8b2baa!important}.theme-ntos .color-bg-pink{background-color:#cf2082!important}.theme-ntos .color-bg-brown{background-color:#8c5836!important}.theme-ntos .color-bg-grey{background-color:#646464!important}.theme-ntos .color-bg-good{background-color:#4d9121!important}.theme-ntos .color-bg-average{background-color:#cd7a0d!important}.theme-ntos .color-bg-bad{background-color:#bd2020!important}.theme-ntos .color-bg-label{background-color:#657a94!important}.theme-ntos .color-bg-gold{background-color:#d6920c!important}.theme-ntos .color-border-black{border-color:#1a1a1a!important}.theme-ntos .color-border-white{border-color:#fff!important}.theme-ntos .color-border-red{border-color:#df3e3e!important}.theme-ntos .color-border-orange{border-color:#f37f33!important}.theme-ntos .color-border-yellow{border-color:#fbda21!important}.theme-ntos .color-border-olive{border-color:#cbe41c!important}.theme-ntos .color-border-green{border-color:#25ca4c!important}.theme-ntos .color-border-teal{border-color:#00d6cc!important}.theme-ntos .color-border-blue{border-color:#2e93de!important}.theme-ntos .color-border-violet{border-color:#7349cf!important}.theme-ntos .color-border-purple{border-color:#ad45d0!important}.theme-ntos .color-border-pink{border-color:#e34da1!important}.theme-ntos .color-border-brown{border-color:#b97447!important}.theme-ntos .color-border-grey{border-color:#848484!important}.theme-ntos .color-border-good{border-color:#68c22d!important}.theme-ntos .color-border-average{border-color:#f29a29!important}.theme-ntos .color-border-bad{border-color:#df3e3e!important}.theme-ntos .color-border-label{border-color:#8b9bb0!important}.theme-ntos .color-border-gold{border-color:#f3b22f!important}.theme-ntos .Section{position:relative;margin-bottom:.5em;background-color:#121922;box-sizing:border-box}.theme-ntos .Section:last-child{margin-bottom:0}.theme-ntos .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.theme-ntos .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-ntos .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-ntos .Section__rest{position:relative}.theme-ntos .Section__content{padding:.66em .5em}.theme-ntos .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-ntos .Section--fill{display:flex;flex-direction:column;height:100%}.theme-ntos .Section--fill>.Section__rest{flex-grow:1}.theme-ntos .Section--fill>.Section__rest>.Section__content{height:100%}.theme-ntos .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-ntos .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-ntos .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-ntos .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-ntos .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-ntos .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-ntos .Section .Section:first-child{margin-top:-.5em}.theme-ntos .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-ntos .Section .Section .Section .Section__titleText{font-size:1em}.theme-ntos .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-ntos .Button:last-child{margin-right:0;margin-bottom:0}.theme-ntos .Button .fa,.theme-ntos .Button .fas,.theme-ntos .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-ntos .Button--hasContent .fa,.theme-ntos .Button--hasContent .fas,.theme-ntos .Button--hasContent .far{margin-right:.25em}.theme-ntos .Button--hasContent.Button--iconRight .fa,.theme-ntos .Button--hasContent.Button--iconRight .fas,.theme-ntos .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-ntos .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-ntos .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-ntos .Button--circular{border-radius:50%}.theme-ntos .Button--compact{padding:0 .25em;line-height:1.333em}.theme-ntos .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-ntos .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-ntos .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--black:hover{background-color:#101010;color:#fff}.theme-ntos .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-ntos .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-ntos .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-ntos .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-ntos .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-ntos .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-ntos .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-ntos .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-ntos .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-ntos .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-ntos .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-ntos .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-ntos .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-ntos .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-ntos .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-ntos .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-ntos .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-ntos .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-ntos .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-ntos .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-ntos .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-ntos .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-ntos .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-ntos .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-ntos .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-ntos .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-ntos .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-ntos .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--label{transition:color .1s,background-color .1s;background-color:#657a94;color:#fff}.theme-ntos .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--label:hover{background-color:#8a9aae;color:#fff}.theme-ntos .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-ntos .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-ntos .Button--color--default{transition:color .1s,background-color .1s;background-color:#384e68;color:#fff}.theme-ntos .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--default:hover{background-color:#4f6885;color:#fff}.theme-ntos .Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-ntos .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--danger{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--danger:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(27,38,51,0);color:rgba(255,255,255,.5)}.theme-ntos .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--transparent:hover{background-color:rgba(44,57,73,.81);color:#fff}.theme-ntos .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(27,38,51,.6);color:rgba(255,255,255,.5)}.theme-ntos .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--translucent:hover{background-color:rgba(48,61,76,.925);color:#fff}.theme-ntos .Button--disabled{background-color:#999!important}.theme-ntos .Button--selected{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-ntos .Button--selected:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--selected:hover{background-color:#2fb94f;color:#fff}.theme-ntos .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-ntos .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-ntos .NumberInput--fluid{display:block}.theme-ntos .NumberInput__content{margin-left:.5em}.theme-ntos .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-ntos .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.theme-ntos .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-ntos .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-ntos .Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-ntos .Input--fluid{display:block;width:auto}.theme-ntos .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-ntos .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-ntos .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-ntos .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-ntos .TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-ntos .TextArea--fluid{display:block;width:auto;height:auto}.theme-ntos .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-ntos .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-ntos .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-ntos .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-ntos .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-ntos .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-ntos .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-ntos .Knob__popupValue,.theme-ntos .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .Knob__popupValue--right{top:.25rem;right:-50%}.theme-ntos .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-ntos .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-ntos .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-ntos .Knob__ringFillPivot{transform:rotate(135deg)}.theme-ntos .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-ntos .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-ntos .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-ntos .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-ntos .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-ntos .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-ntos .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-ntos .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-ntos .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-ntos .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-ntos .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-ntos .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-ntos .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-ntos .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-ntos .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-ntos .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-ntos .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-ntos .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.theme-ntos .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-ntos .Slider:not(.Slider__disabled){cursor:e-resize}.theme-ntos .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-ntos .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-ntos .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-ntos .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-ntos .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-ntos .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-ntos .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-ntos .ProgressBar--color--default{border:.0833333333em solid #3e6189}.theme-ntos .ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.theme-ntos .ProgressBar--color--disabled{border:1px solid #999}.theme-ntos .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-ntos .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-ntos .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-ntos .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-ntos .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-ntos .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-ntos .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-ntos .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-ntos .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-ntos .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-ntos .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-ntos .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-ntos .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-ntos .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-ntos .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-ntos .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-ntos .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-ntos .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-ntos .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-ntos .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-ntos .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-ntos .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-ntos .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-ntos .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-ntos .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-ntos .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-ntos .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-ntos .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-ntos .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-ntos .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-ntos .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-ntos .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--label{border:.0833333333em solid #657a94!important}.theme-ntos .ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.theme-ntos .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-ntos .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-ntos .Chat{color:#abc6ec}.theme-ntos .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-ntos .Chat__badge:before{content:"x"}.theme-ntos .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-ntos .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-ntos .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-ntos .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#121922}.theme-ntos .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-ntos .Chat__highlight{color:#000}.theme-ntos .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-ntos .ChatMessage{word-wrap:break-word}.theme-ntos .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-ntos .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-ntos html,.theme-ntos body{scrollbar-color:#2a3b4f #141d26}.theme-ntos .Layout,.theme-ntos .Layout *{scrollbar-base-color:#141d26;scrollbar-face-color:#2a3b4f;scrollbar-3dlight-color:#1b2633;scrollbar-highlight-color:#1b2633;scrollbar-track-color:#141d26;scrollbar-arrow-color:#7290b4;scrollbar-shadow-color:#2a3b4f}.theme-ntos .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-ntos .Layout__content--flexRow{display:flex;flex-flow:row}.theme-ntos .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-ntos .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-ntos .Layout__content--noMargin{margin:0}.theme-ntos .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#1b2633;background-image:linear-gradient(to bottom,#1b2633,#1b2633)}.theme-ntos .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-ntos .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-ntos .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-ntos .Window__contentPadding:after{height:0}.theme-ntos .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-ntos .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(50,63,78,.25);pointer-events:none}.theme-ntos .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-ntos .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-ntos .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-ntos .TitleBar{background-color:#1b2633;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-ntos .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#1b2633;transition:color .25s,background-color .25s}.theme-ntos .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-ntos .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-ntos .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-ntos .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-ntos .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-ntos .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-ntos .boxed_message{background:#1c242e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-ntos .boxed_message.left_align_text{text-align:left}.theme-ntos .boxed_message.red_border{background:#2e1c1c;border-color:#a00}.theme-ntos .boxed_message.green_border{background:#1c2e22;border-color:#0f0}.theme-ntos .boxed_message.purple_border{background:#221c2e;border-color:#8000ff}.theme-ntos .boxed_message.notice_border{background:#1f2633;border-color:#6685f5}.theme-ntos .boxed_message.thick_border{border-width:thick}.theme-syndicate .color-black{color:#1a1a1a!important}.theme-syndicate .color-white{color:#fff!important}.theme-syndicate .color-red{color:#df3e3e!important}.theme-syndicate .color-orange{color:#f37f33!important}.theme-syndicate .color-yellow{color:#fbda21!important}.theme-syndicate .color-olive{color:#cbe41c!important}.theme-syndicate .color-green{color:#25ca4c!important}.theme-syndicate .color-teal{color:#00d6cc!important}.theme-syndicate .color-blue{color:#2e93de!important}.theme-syndicate .color-violet{color:#7349cf!important}.theme-syndicate .color-purple{color:#ad45d0!important}.theme-syndicate .color-pink{color:#e34da1!important}.theme-syndicate .color-brown{color:#b97447!important}.theme-syndicate .color-grey{color:#848484!important}.theme-syndicate .color-good{color:#68c22d!important}.theme-syndicate .color-average{color:#f29a29!important}.theme-syndicate .color-bad{color:#df3e3e!important}.theme-syndicate .color-label{color:#8b9bb0!important}.theme-syndicate .color-gold{color:#f3b22f!important}.theme-syndicate .color-bg-black{background-color:#000!important}.theme-syndicate .color-bg-white{background-color:#d9d9d9!important}.theme-syndicate .color-bg-red{background-color:#bd2020!important}.theme-syndicate .color-bg-orange{background-color:#d95e0c!important}.theme-syndicate .color-bg-yellow{background-color:#d9b804!important}.theme-syndicate .color-bg-olive{background-color:#9aad14!important}.theme-syndicate .color-bg-green{background-color:#1b9638!important}.theme-syndicate .color-bg-teal{background-color:#009a93!important}.theme-syndicate .color-bg-blue{background-color:#1c71b1!important}.theme-syndicate .color-bg-violet{background-color:#552dab!important}.theme-syndicate .color-bg-purple{background-color:#8b2baa!important}.theme-syndicate .color-bg-pink{background-color:#cf2082!important}.theme-syndicate .color-bg-brown{background-color:#8c5836!important}.theme-syndicate .color-bg-grey{background-color:#646464!important}.theme-syndicate .color-bg-good{background-color:#4d9121!important}.theme-syndicate .color-bg-average{background-color:#cd7a0d!important}.theme-syndicate .color-bg-bad{background-color:#bd2020!important}.theme-syndicate .color-bg-label{background-color:#657a94!important}.theme-syndicate .color-bg-gold{background-color:#d6920c!important}.theme-syndicate .color-border-black{border-color:#1a1a1a!important}.theme-syndicate .color-border-white{border-color:#fff!important}.theme-syndicate .color-border-red{border-color:#df3e3e!important}.theme-syndicate .color-border-orange{border-color:#f37f33!important}.theme-syndicate .color-border-yellow{border-color:#fbda21!important}.theme-syndicate .color-border-olive{border-color:#cbe41c!important}.theme-syndicate .color-border-green{border-color:#25ca4c!important}.theme-syndicate .color-border-teal{border-color:#00d6cc!important}.theme-syndicate .color-border-blue{border-color:#2e93de!important}.theme-syndicate .color-border-violet{border-color:#7349cf!important}.theme-syndicate .color-border-purple{border-color:#ad45d0!important}.theme-syndicate .color-border-pink{border-color:#e34da1!important}.theme-syndicate .color-border-brown{border-color:#b97447!important}.theme-syndicate .color-border-grey{border-color:#848484!important}.theme-syndicate .color-border-good{border-color:#68c22d!important}.theme-syndicate .color-border-average{border-color:#f29a29!important}.theme-syndicate .color-border-bad{border-color:#df3e3e!important}.theme-syndicate .color-border-label{border-color:#8b9bb0!important}.theme-syndicate .color-border-gold{border-color:#f3b22f!important}.theme-syndicate .Section{position:relative;margin-bottom:.5em;background-color:#2b0101;box-sizing:border-box}.theme-syndicate .Section:last-child{margin-bottom:0}.theme-syndicate .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #397439}.theme-syndicate .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-syndicate .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-syndicate .Section__rest{position:relative}.theme-syndicate .Section__content{padding:.66em .5em}.theme-syndicate .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-syndicate .Section--fill{display:flex;flex-direction:column;height:100%}.theme-syndicate .Section--fill>.Section__rest{flex-grow:1}.theme-syndicate .Section--fill>.Section__rest>.Section__content{height:100%}.theme-syndicate .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-syndicate .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-syndicate .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-syndicate .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-syndicate .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-syndicate .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-syndicate .Section .Section:first-child{margin-top:-.5em}.theme-syndicate .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-syndicate .Section .Section .Section .Section__titleText{font-size:1em}.theme-syndicate .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-syndicate .Button:last-child{margin-right:0;margin-bottom:0}.theme-syndicate .Button .fa,.theme-syndicate .Button .fas,.theme-syndicate .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-syndicate .Button--hasContent .fa,.theme-syndicate .Button--hasContent .fas,.theme-syndicate .Button--hasContent .far{margin-right:.25em}.theme-syndicate .Button--hasContent.Button--iconRight .fa,.theme-syndicate .Button--hasContent.Button--iconRight .fas,.theme-syndicate .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-syndicate .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-syndicate .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-syndicate .Button--circular{border-radius:50%}.theme-syndicate .Button--compact{padding:0 .25em;line-height:1.333em}.theme-syndicate .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-syndicate .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-syndicate .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--black:hover{background-color:#101010;color:#fff}.theme-syndicate .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-syndicate .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-syndicate .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-syndicate .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-syndicate .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-syndicate .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-syndicate .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-syndicate .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-syndicate .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-syndicate .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-syndicate .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-syndicate .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-syndicate .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-syndicate .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-syndicate .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-syndicate .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-syndicate .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-syndicate .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-syndicate .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-syndicate .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-syndicate .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-syndicate .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-syndicate .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-syndicate .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-syndicate .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-syndicate .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-syndicate .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-syndicate .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-syndicate .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-syndicate .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-syndicate .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--label{transition:color .1s,background-color .1s;background-color:#657a94;color:#fff}.theme-syndicate .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--label:hover{background-color:#8a9aae;color:#fff}.theme-syndicate .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-syndicate .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-syndicate .Button--color--default{transition:color .1s,background-color .1s;background-color:#397439;color:#fff}.theme-syndicate .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--default:hover{background-color:#509350;color:#fff}.theme-syndicate .Button--color--caution{transition:color .1s,background-color .1s;background-color:#be6209;color:#fff}.theme-syndicate .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-syndicate .Button--color--danger{transition:color .1s,background-color .1s;background-color:#9a9d00;color:#fff}.theme-syndicate .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-syndicate .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(77,2,2,0);color:rgba(255,255,255,.5)}.theme-syndicate .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--transparent:hover{background-color:rgba(103,14,14,.81);color:#fff}.theme-syndicate .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(77,2,2,.6);color:rgba(255,255,255,.5)}.theme-syndicate .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--translucent:hover{background-color:rgba(105,20,20,.925);color:#fff}.theme-syndicate .Button--disabled{background-color:#363636!important}.theme-syndicate .Button--selected{transition:color .1s,background-color .1s;background-color:#9d0808;color:#fff}.theme-syndicate .Button--selected:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--selected:hover{background-color:#c11919;color:#fff}.theme-syndicate .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-syndicate .NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#fff;background-color:#910101;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.theme-syndicate .NoticeBox--color--black{color:#fff;background-color:#000}.theme-syndicate .NoticeBox--color--white{color:#000;background-color:#b3b3b3}.theme-syndicate .NoticeBox--color--red{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--orange{color:#fff;background-color:#854114}.theme-syndicate .NoticeBox--color--yellow{color:#000;background-color:#83710d}.theme-syndicate .NoticeBox--color--olive{color:#000;background-color:#576015}.theme-syndicate .NoticeBox--color--green{color:#fff;background-color:#174e24}.theme-syndicate .NoticeBox--color--teal{color:#fff;background-color:#064845}.theme-syndicate .NoticeBox--color--blue{color:#fff;background-color:#1b4565}.theme-syndicate .NoticeBox--color--violet{color:#fff;background-color:#3b2864}.theme-syndicate .NoticeBox--color--purple{color:#fff;background-color:#542663}.theme-syndicate .NoticeBox--color--pink{color:#fff;background-color:#802257}.theme-syndicate .NoticeBox--color--brown{color:#fff;background-color:#4c3729}.theme-syndicate .NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.theme-syndicate .NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.theme-syndicate .NoticeBox--color--average{color:#fff;background-color:#7b4e13}.theme-syndicate .NoticeBox--color--bad{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--label{color:#fff;background-color:#53565a}.theme-syndicate .NoticeBox--color--gold{color:#fff;background-color:#825d13}.theme-syndicate .NoticeBox--type--info{color:#fff;background-color:#235982}.theme-syndicate .NoticeBox--type--success{color:#fff;background-color:#1e662f}.theme-syndicate .NoticeBox--type--warning{color:#fff;background-color:#a95219}.theme-syndicate .NoticeBox--type--danger{color:#fff;background-color:#8f2828}.theme-syndicate .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;color:#87ce87;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-syndicate .NumberInput--fluid{display:block}.theme-syndicate .NumberInput__content{margin-left:.5em}.theme-syndicate .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-syndicate .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #87ce87;background-color:#87ce87}.theme-syndicate .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-syndicate .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-syndicate .Input--disabled{color:#777;border-color:#6b6b6b;border-color:rgba(107,107,107,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-syndicate .Input--fluid{display:block;width:auto}.theme-syndicate .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-syndicate .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-syndicate .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-syndicate .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-syndicate .TextArea{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-syndicate .TextArea--fluid{display:block;width:auto;height:auto}.theme-syndicate .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-syndicate .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-syndicate .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-syndicate .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-syndicate .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-syndicate .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-syndicate .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-syndicate .Knob__popupValue,.theme-syndicate .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .Knob__popupValue--right{top:.25rem;right:-50%}.theme-syndicate .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-syndicate .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-syndicate .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-syndicate .Knob__ringFillPivot{transform:rotate(135deg)}.theme-syndicate .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-syndicate .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-syndicate .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-syndicate .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-syndicate .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-syndicate .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-syndicate .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-syndicate .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-syndicate .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-syndicate .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-syndicate .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-syndicate .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-syndicate .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-syndicate .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-syndicate .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-syndicate .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-syndicate .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-syndicate .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.theme-syndicate .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-syndicate .Slider:not(.Slider__disabled){cursor:e-resize}.theme-syndicate .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-syndicate .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-syndicate .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-syndicate .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,.5);transition:border-color .5s}.theme-syndicate .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-syndicate .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-syndicate .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-syndicate .ProgressBar--color--default{border:.0833333333em solid #306330}.theme-syndicate .ProgressBar--color--default .ProgressBar__fill{background-color:#306330}.theme-syndicate .ProgressBar--color--disabled{border:1px solid #999}.theme-syndicate .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-syndicate .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-syndicate .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-syndicate .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-syndicate .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-syndicate .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-syndicate .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-syndicate .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-syndicate .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-syndicate .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-syndicate .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-syndicate .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-syndicate .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-syndicate .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-syndicate .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-syndicate .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-syndicate .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-syndicate .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-syndicate .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-syndicate .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-syndicate .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-syndicate .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-syndicate .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-syndicate .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-syndicate .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-syndicate .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-syndicate .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-syndicate .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-syndicate .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-syndicate .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-syndicate .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-syndicate .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--label{border:.0833333333em solid #657a94!important}.theme-syndicate .ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.theme-syndicate .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-syndicate .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-syndicate .Chat{color:#abc6ec}.theme-syndicate .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-syndicate .Chat__badge:before{content:"x"}.theme-syndicate .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-syndicate .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-syndicate .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-syndicate .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#2b0101}.theme-syndicate .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-syndicate .Chat__highlight{color:#000}.theme-syndicate .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-syndicate .ChatMessage{word-wrap:break-word}.theme-syndicate .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-syndicate .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-syndicate html,.theme-syndicate body{scrollbar-color:#770303 #3a0202}.theme-syndicate .Layout,.theme-syndicate .Layout *{scrollbar-base-color:#3a0202;scrollbar-face-color:#770303;scrollbar-3dlight-color:#4d0202;scrollbar-highlight-color:#4d0202;scrollbar-track-color:#3a0202;scrollbar-arrow-color:#fa2d2d;scrollbar-shadow-color:#770303}.theme-syndicate .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-syndicate .Layout__content--flexRow{display:flex;flex-flow:row}.theme-syndicate .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-syndicate .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-syndicate .Layout__content--noMargin{margin:0}.theme-syndicate .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#4d0202;background-image:linear-gradient(to bottom,#4d0202,#4d0202)}.theme-syndicate .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-syndicate .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-syndicate .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-syndicate .Window__contentPadding:after{height:0}.theme-syndicate .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-syndicate .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(108,22,22,.25);pointer-events:none}.theme-syndicate .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-syndicate .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-syndicate .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-syndicate .TitleBar{background-color:#910101;border-bottom:1px solid #161616;box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-syndicate .TitleBar__clickable{color:rgba(255,255,255,.5);background-color:#910101;transition:color .25s,background-color .25s}.theme-syndicate .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-syndicate .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,255,255,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-syndicate .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-syndicate .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-syndicate .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-syndicate .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-syndicate .adminooc{color:#29ccbe}.theme-syndicate .debug{color:#8f39e6}.theme-syndicate .boxed_message{background:rgba(20,20,35,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-syndicate .boxed_message.left_align_text{text-align:left}.theme-syndicate .boxed_message.red_border{background:rgba(0,0,0,.2);border-color:red}.theme-syndicate .boxed_message.green_border{background:rgba(0,75,0,.25);border-color:#0f0}.theme-syndicate .boxed_message.purple_border{background:rgba(25,0,50,.25);border-color:#8000ff}.theme-syndicate .boxed_message.notice_border{background:rgba(0,0,75,.25);border-color:#6685f5}.theme-syndicate .boxed_message.thick_border{border-width:thick}.theme-paradise .color-black{color:#1a1a1a!important}.theme-paradise .color-white{color:#fff!important}.theme-paradise .color-red{color:#df3e3e!important}.theme-paradise .color-orange{color:#f37f33!important}.theme-paradise .color-yellow{color:#fbda21!important}.theme-paradise .color-olive{color:#cbe41c!important}.theme-paradise .color-green{color:#25ca4c!important}.theme-paradise .color-teal{color:#00d6cc!important}.theme-paradise .color-blue{color:#2e93de!important}.theme-paradise .color-violet{color:#7349cf!important}.theme-paradise .color-purple{color:#ad45d0!important}.theme-paradise .color-pink{color:#e34da1!important}.theme-paradise .color-brown{color:#b97447!important}.theme-paradise .color-grey{color:#848484!important}.theme-paradise .color-good{color:#68c22d!important}.theme-paradise .color-average{color:#f29a29!important}.theme-paradise .color-bad{color:#df3e3e!important}.theme-paradise .color-label{color:#955d4b!important}.theme-paradise .color-gold{color:#f3b22f!important}.theme-paradise .color-bg-black{background-color:#000!important}.theme-paradise .color-bg-white{background-color:#d9d9d9!important}.theme-paradise .color-bg-red{background-color:#bd2020!important}.theme-paradise .color-bg-orange{background-color:#d95e0c!important}.theme-paradise .color-bg-yellow{background-color:#d9b804!important}.theme-paradise .color-bg-olive{background-color:#9aad14!important}.theme-paradise .color-bg-green{background-color:#1b9638!important}.theme-paradise .color-bg-teal{background-color:#009a93!important}.theme-paradise .color-bg-blue{background-color:#1c71b1!important}.theme-paradise .color-bg-violet{background-color:#552dab!important}.theme-paradise .color-bg-purple{background-color:#8b2baa!important}.theme-paradise .color-bg-pink{background-color:#cf2082!important}.theme-paradise .color-bg-brown{background-color:#8c5836!important}.theme-paradise .color-bg-grey{background-color:#646464!important}.theme-paradise .color-bg-good{background-color:#4d9121!important}.theme-paradise .color-bg-average{background-color:#cd7a0d!important}.theme-paradise .color-bg-bad{background-color:#bd2020!important}.theme-paradise .color-bg-label{background-color:#6d4436!important}.theme-paradise .color-bg-gold{background-color:#d6920c!important}.theme-paradise .color-border-black{border-color:#1a1a1a!important}.theme-paradise .color-border-white{border-color:#fff!important}.theme-paradise .color-border-red{border-color:#df3e3e!important}.theme-paradise .color-border-orange{border-color:#f37f33!important}.theme-paradise .color-border-yellow{border-color:#fbda21!important}.theme-paradise .color-border-olive{border-color:#cbe41c!important}.theme-paradise .color-border-green{border-color:#25ca4c!important}.theme-paradise .color-border-teal{border-color:#00d6cc!important}.theme-paradise .color-border-blue{border-color:#2e93de!important}.theme-paradise .color-border-violet{border-color:#7349cf!important}.theme-paradise .color-border-purple{border-color:#ad45d0!important}.theme-paradise .color-border-pink{border-color:#e34da1!important}.theme-paradise .color-border-brown{border-color:#b97447!important}.theme-paradise .color-border-grey{border-color:#848484!important}.theme-paradise .color-border-good{border-color:#68c22d!important}.theme-paradise .color-border-average{border-color:#f29a29!important}.theme-paradise .color-border-bad{border-color:#df3e3e!important}.theme-paradise .color-border-label{border-color:#955d4b!important}.theme-paradise .color-border-gold{border-color:#f3b22f!important}.theme-paradise .Section{position:relative;margin-bottom:.5em;background-color:#40071a;background-color:rgba(0,0,0,.5);box-sizing:border-box}.theme-paradise .Section:last-child{margin-bottom:0}.theme-paradise .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #208080}.theme-paradise .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-paradise .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-paradise .Section__rest{position:relative}.theme-paradise .Section__content{padding:.66em .5em}.theme-paradise .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-paradise .Section--fill{display:flex;flex-direction:column;height:100%}.theme-paradise .Section--fill>.Section__rest{flex-grow:1}.theme-paradise .Section--fill>.Section__rest>.Section__content{height:100%}.theme-paradise .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-paradise .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-paradise .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-paradise .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-paradise .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-paradise .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-paradise .Section .Section:first-child{margin-top:-.5em}.theme-paradise .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-paradise .Section .Section .Section .Section__titleText{font-size:1em}.theme-paradise .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-paradise .Button:last-child{margin-right:0;margin-bottom:0}.theme-paradise .Button .fa,.theme-paradise .Button .fas,.theme-paradise .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-paradise .Button--hasContent .fa,.theme-paradise .Button--hasContent .fas,.theme-paradise .Button--hasContent .far{margin-right:.25em}.theme-paradise .Button--hasContent.Button--iconRight .fa,.theme-paradise .Button--hasContent.Button--iconRight .fas,.theme-paradise .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-paradise .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-paradise .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-paradise .Button--circular{border-radius:50%}.theme-paradise .Button--compact{padding:0 .25em;line-height:1.333em}.theme-paradise .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-paradise .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-paradise .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--black:hover{background-color:#101010;color:#fff}.theme-paradise .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-paradise .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-paradise .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-paradise .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-paradise .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-paradise .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-paradise .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-paradise .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-paradise .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-paradise .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-paradise .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-paradise .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-paradise .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-paradise .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-paradise .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-paradise .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-paradise .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-paradise .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-paradise .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-paradise .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-paradise .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-paradise .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-paradise .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-paradise .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-paradise .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-paradise .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-paradise .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-paradise .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-paradise .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-paradise .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--label{transition:color .1s,background-color .1s;background-color:#6d4436;color:#fff}.theme-paradise .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--label:hover{background-color:#8b5d4d;color:#fff}.theme-paradise .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-paradise .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-paradise .Button--color--default{transition:color .1s,background-color .1s;background-color:#208080;color:#fff}.theme-paradise .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--default:hover{background-color:#34a0a0;color:#fff}.theme-paradise .Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-paradise .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--danger{transition:color .1s,background-color .1s;background-color:#8c1eff;color:#fff}.theme-paradise .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--danger:hover{background-color:#ae61ff;color:#fff}.theme-paradise .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(128,13,51,0);color:rgba(255,255,255,.5)}.theme-paradise .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--transparent:hover{background-color:rgba(164,27,73,.81);color:#fff}.theme-paradise .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(128,13,51,.6);color:rgba(255,255,255,.5)}.theme-paradise .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--translucent:hover{background-color:rgba(164,32,76,.925);color:#fff}.theme-paradise .Button--disabled{background-color:#999!important}.theme-paradise .Button--selected{transition:color .1s,background-color .1s;background-color:#bf6030;color:#fff}.theme-paradise .Button--selected:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--selected:hover{background-color:#d4835a;color:#fff}.theme-paradise .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-paradise .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;color:#e65c2e;background-color:rgba(0,0,0,.25);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-paradise .NumberInput--fluid{display:block}.theme-paradise .NumberInput__content{margin-left:.5em}.theme-paradise .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-paradise .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #e65c2e;background-color:#e65c2e}.theme-paradise .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,.25);color:#fff;text-align:right}.theme-paradise .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-paradise .Input--disabled{color:#777;border-color:#4a4a4a;border-color:rgba(74,74,74,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-paradise .Input--fluid{display:block;width:auto}.theme-paradise .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-paradise .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-paradise .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-paradise .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-paradise .TextArea{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-paradise .TextArea--fluid{display:block;width:auto;height:auto}.theme-paradise .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-paradise .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-paradise .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-paradise .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-paradise .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-paradise .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-paradise .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-paradise .Knob__popupValue,.theme-paradise .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .Knob__popupValue--right{top:.25rem;right:-50%}.theme-paradise .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-paradise .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-paradise .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-paradise .Knob__ringFillPivot{transform:rotate(135deg)}.theme-paradise .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-paradise .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-paradise .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-paradise .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-paradise .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-paradise .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-paradise .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-paradise .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-paradise .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-paradise .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-paradise .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-paradise .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-paradise .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-paradise .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-paradise .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-paradise .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-paradise .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-paradise .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--label .Knob__ringFill{stroke:#955d4b}.theme-paradise .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-paradise .Slider:not(.Slider__disabled){cursor:e-resize}.theme-paradise .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-paradise .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-paradise .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-paradise .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-paradise .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-paradise .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-paradise .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-paradise .ProgressBar--color--default{border:.0833333333em solid #1b6d6d}.theme-paradise .ProgressBar--color--default .ProgressBar__fill{background-color:#1b6d6d}.theme-paradise .ProgressBar--color--disabled{border:1px solid #999}.theme-paradise .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-paradise .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-paradise .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-paradise .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-paradise .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-paradise .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-paradise .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-paradise .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-paradise .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-paradise .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-paradise .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-paradise .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-paradise .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-paradise .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-paradise .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-paradise .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-paradise .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-paradise .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-paradise .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-paradise .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-paradise .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-paradise .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-paradise .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-paradise .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-paradise .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-paradise .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-paradise .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-paradise .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-paradise .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-paradise .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-paradise .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-paradise .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--label{border:.0833333333em solid #6d4436!important}.theme-paradise .ProgressBar--color--label .ProgressBar__fill{background-color:#6d4436}.theme-paradise .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-paradise .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-paradise .Chat{color:#abc6ec}.theme-paradise .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-paradise .Chat__badge:before{content:"x"}.theme-paradise .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-paradise .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-paradise .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-paradise .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#fff;background-color:#db2828}.theme-paradise .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-paradise .Chat__highlight{color:#000}.theme-paradise .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-paradise .ChatMessage{word-wrap:break-word}.theme-paradise .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-paradise .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-paradise html,.theme-paradise body{scrollbar-color:#cb1551 #680b29}.theme-paradise .Layout,.theme-paradise .Layout *{scrollbar-base-color:#680b29;scrollbar-face-color:#99103d;scrollbar-3dlight-color:#800d33;scrollbar-highlight-color:#800d33;scrollbar-track-color:#680b29;scrollbar-arrow-color:#ea2e6c;scrollbar-shadow-color:#99103d}.theme-paradise .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-paradise .Layout__content--flexRow{display:flex;flex-flow:row}.theme-paradise .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-paradise .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-paradise .Layout__content--noMargin{margin:0}.theme-paradise .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#800d33;background-image:linear-gradient(to bottom,#80014b,#80460d)}.theme-paradise .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-paradise .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-paradise .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-paradise .Window__contentPadding:after{height:0}.theme-paradise .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-paradise .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(166,34,78,.25);pointer-events:none}.theme-paradise .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-paradise .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-paradise .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-paradise .TitleBar{background-color:#800d33;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-paradise .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#800d33;transition:color .25s,background-color .25s}.theme-paradise .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-paradise .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-paradise .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-paradise .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-paradise .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-paradise .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-paradise .adminooc{color:#29ccbe}.theme-paradise .debug{color:#8f39e6}.theme-paradise .boxed_message{background:rgba(0,0,0,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-paradise .boxed_message.left_align_text{text-align:left}.theme-paradise .boxed_message.red_border{background:rgba(0,0,0,.25);border-color:#a00}.theme-paradise .boxed_message.green_border{background:rgba(0,0,0,.25);border-color:#0f0}.theme-paradise .boxed_message.purple_border{background:rgba(0,0,0,.25);border-color:#8000ff}.theme-paradise .boxed_message.notice_border{background:rgba(0,0,0,.25);border-color:#6685f5}.theme-paradise .boxed_message.thick_border{border-width:thick} +html,body{box-sizing:border-box;height:100%;margin:0;font-size:12px}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif}*,*:before,*:after{box-sizing:inherit}h1,h2,h3,h4,h5,h6{display:block;margin:0;padding:6px 0;padding:.5rem 0}h1{font-size:18px;font-size:1.5rem}h2{font-size:16px;font-size:1.333rem}h3{font-size:14px;font-size:1.167rem}h4{font-size:12px;font-size:1rem}td,th{vertical-align:baseline;text-align:left}.candystripe:nth-child(odd){background-color:rgba(0,0,0,.25)}.color-black{color:#1a1a1a!important}.color-white{color:#fff!important}.color-red{color:#df3e3e!important}.color-orange{color:#f37f33!important}.color-yellow{color:#fbda21!important}.color-olive{color:#cbe41c!important}.color-green{color:#25ca4c!important}.color-teal{color:#00d6cc!important}.color-blue{color:#2e93de!important}.color-violet{color:#7349cf!important}.color-purple{color:#ad45d0!important}.color-pink{color:#e34da1!important}.color-brown{color:#b97447!important}.color-grey{color:#848484!important}.color-good{color:#68c22d!important}.color-average{color:#f29a29!important}.color-bad{color:#df3e3e!important}.color-label{color:#8b9bb0!important}.color-gold{color:#f3b22f!important}.color-bg-black{background-color:#000!important}.color-bg-white{background-color:#d9d9d9!important}.color-bg-red{background-color:#bd2020!important}.color-bg-orange{background-color:#d95e0c!important}.color-bg-yellow{background-color:#d9b804!important}.color-bg-olive{background-color:#9aad14!important}.color-bg-green{background-color:#1b9638!important}.color-bg-teal{background-color:#009a93!important}.color-bg-blue{background-color:#1c71b1!important}.color-bg-violet{background-color:#552dab!important}.color-bg-purple{background-color:#8b2baa!important}.color-bg-pink{background-color:#cf2082!important}.color-bg-brown{background-color:#8c5836!important}.color-bg-grey{background-color:#646464!important}.color-bg-good{background-color:#4d9121!important}.color-bg-average{background-color:#cd7a0d!important}.color-bg-bad{background-color:#bd2020!important}.color-bg-label{background-color:#657a94!important}.color-bg-gold{background-color:#d6920c!important}.color-border-black{border-color:#1a1a1a!important}.color-border-white{border-color:#fff!important}.color-border-red{border-color:#df3e3e!important}.color-border-orange{border-color:#f37f33!important}.color-border-yellow{border-color:#fbda21!important}.color-border-olive{border-color:#cbe41c!important}.color-border-green{border-color:#25ca4c!important}.color-border-teal{border-color:#00d6cc!important}.color-border-blue{border-color:#2e93de!important}.color-border-violet{border-color:#7349cf!important}.color-border-purple{border-color:#ad45d0!important}.color-border-pink{border-color:#e34da1!important}.color-border-brown{border-color:#b97447!important}.color-border-grey{border-color:#848484!important}.color-border-good{border-color:#68c22d!important}.color-border-average{border-color:#f29a29!important}.color-border-bad{border-color:#df3e3e!important}.color-border-label{border-color:#8b9bb0!important}.color-border-gold{border-color:#f3b22f!important}.debug-layout,.debug-layout *:not(g):not(path){color:rgba(255,255,255,.9)!important;background:rgba(0,0,0,0)!important;outline:1px solid rgba(255,255,255,.5)!important;box-shadow:none!important;filter:none!important}.debug-layout:hover,.debug-layout *:not(g):not(path):hover{outline-color:rgba(255,255,255,.8)!important}.outline-dotted{outline-style:dotted!important}.outline-dashed{outline-style:dashed!important}.outline-solid{outline-style:solid!important}.outline-double{outline-style:double!important}.outline-groove{outline-style:groove!important}.outline-ridge{outline-style:ridge!important}.outline-inset{outline-style:inset!important}.outline-outset{outline-style:outset!important}.outline-color-black{outline:.167rem solid #1a1a1a!important}.outline-color-white{outline:.167rem solid #fff!important}.outline-color-red{outline:.167rem solid #df3e3e!important}.outline-color-orange{outline:.167rem solid #f37f33!important}.outline-color-yellow{outline:.167rem solid #fbda21!important}.outline-color-olive{outline:.167rem solid #cbe41c!important}.outline-color-green{outline:.167rem solid #25ca4c!important}.outline-color-teal{outline:.167rem solid #00d6cc!important}.outline-color-blue{outline:.167rem solid #2e93de!important}.outline-color-violet{outline:.167rem solid #7349cf!important}.outline-color-purple{outline:.167rem solid #ad45d0!important}.outline-color-pink{outline:.167rem solid #e34da1!important}.outline-color-brown{outline:.167rem solid #b97447!important}.outline-color-grey{outline:.167rem solid #848484!important}.outline-color-good{outline:.167rem solid #68c22d!important}.outline-color-average{outline:.167rem solid #f29a29!important}.outline-color-bad{outline:.167rem solid #df3e3e!important}.outline-color-label{outline:.167rem solid #8b9bb0!important}.outline-color-gold{outline:.167rem solid #f3b22f!important}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-baseline{text-align:baseline}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-pre{white-space:pre}.text-bold{font-weight:700}.text-italic{font-style:italic}.text-underline{text-decoration:underline}.BlockQuote{color:#8b9bb0;border-left:.1666666667em solid #8b9bb0;padding-left:.5em;margin-bottom:.5em}.BlockQuote:last-child{margin-bottom:0}.Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.Button:last-child{margin-right:0;margin-bottom:0}.Button .fa,.Button .fas,.Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.Button--hasContent .fa,.Button--hasContent .fas,.Button--hasContent .far{margin-right:.25em}.Button--hasContent.Button--iconRight .fa,.Button--hasContent.Button--iconRight .fas,.Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.Button--fluid{display:block;margin-left:0;margin-right:0}.Button--circular{border-radius:50%}.Button--compact{padding:0 .25em;line-height:1.333em}.Button--multiLine{white-space:normal;word-wrap:break-word}.Button--modal{float:right;z-index:1;margin-top:-.5rem}.Button--color--black{background-color:#000;color:#fff;transition:color .2s,background-color .2s}.Button--color--black:hover{background-color:#101010;color:#fff}.Button--color--black--translucent{background-color:rgba(0,0,0,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--black--translucent:hover{background-color:rgba(16,16,16,.5);color:#fff}.Button--color--white{background-color:#d9d9d9;color:#000;transition:color .2s,background-color .2s}.Button--color--white:hover{background-color:#f8f8f8;color:#000}.Button--color--white--translucent{background-color:rgba(217,217,217,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--white--translucent:hover{background-color:rgba(248,248,248,.5);color:#fff}.Button--color--red{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.Button--color--red:hover{background-color:#d93f3f;color:#fff}.Button--color--red--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--red--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.Button--color--orange{background-color:#d95e0c;color:#fff;transition:color .2s,background-color .2s}.Button--color--orange:hover{background-color:#ef7e33;color:#fff}.Button--color--orange--translucent{background-color:rgba(217,94,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--orange--translucent:hover{background-color:rgba(239,126,51,.5);color:#fff}.Button--color--yellow{background-color:#d9b804;color:#000;transition:color .2s,background-color .2s}.Button--color--yellow:hover{background-color:#f5d523;color:#000}.Button--color--yellow--translucent{background-color:rgba(217,184,4,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--yellow--translucent:hover{background-color:rgba(245,213,35,.5);color:#fff}.Button--color--olive{background-color:#9aad14;color:#fff;transition:color .2s,background-color .2s}.Button--color--olive:hover{background-color:#bdd327;color:#fff}.Button--color--olive--translucent{background-color:rgba(154,173,20,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--olive--translucent:hover{background-color:rgba(189,211,39,.5);color:#fff}.Button--color--green{background-color:#1b9638;color:#fff;transition:color .2s,background-color .2s}.Button--color--green:hover{background-color:#2fb94f;color:#fff}.Button--color--green--translucent{background-color:rgba(27,150,56,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--green--translucent:hover{background-color:rgba(47,185,79,.5);color:#fff}.Button--color--teal{background-color:#009a93;color:#fff;transition:color .2s,background-color .2s}.Button--color--teal:hover{background-color:#10bdb6;color:#fff}.Button--color--teal--translucent{background-color:rgba(0,154,147,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--teal--translucent:hover{background-color:rgba(16,189,182,.5);color:#fff}.Button--color--blue{background-color:#1c71b1;color:#fff;transition:color .2s,background-color .2s}.Button--color--blue:hover{background-color:#308fd6;color:#fff}.Button--color--blue--translucent{background-color:rgba(28,113,177,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--blue--translucent:hover{background-color:rgba(48,143,214,.5);color:#fff}.Button--color--violet{background-color:#552dab;color:#fff;transition:color .2s,background-color .2s}.Button--color--violet:hover{background-color:#7249ca;color:#fff}.Button--color--violet--translucent{background-color:rgba(85,45,171,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--violet--translucent:hover{background-color:rgba(114,73,202,.5);color:#fff}.Button--color--purple{background-color:#8b2baa;color:#fff;transition:color .2s,background-color .2s}.Button--color--purple:hover{background-color:#aa46ca;color:#fff}.Button--color--purple--translucent{background-color:rgba(139,43,170,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--purple--translucent:hover{background-color:rgba(170,70,202,.5);color:#fff}.Button--color--pink{background-color:#cf2082;color:#fff;transition:color .2s,background-color .2s}.Button--color--pink:hover{background-color:#e04ca0;color:#fff}.Button--color--pink--translucent{background-color:rgba(207,32,130,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--pink--translucent:hover{background-color:rgba(224,76,160,.5);color:#fff}.Button--color--brown{background-color:#8c5836;color:#fff;transition:color .2s,background-color .2s}.Button--color--brown:hover{background-color:#ae724c;color:#fff}.Button--color--brown--translucent{background-color:rgba(140,88,54,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--brown--translucent:hover{background-color:rgba(174,114,76,.5);color:#fff}.Button--color--grey{background-color:#646464;color:#fff;transition:color .2s,background-color .2s}.Button--color--grey:hover{background-color:#818181;color:#fff}.Button--color--grey--translucent{background-color:rgba(100,100,100,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--grey--translucent:hover{background-color:rgba(129,129,129,.5);color:#fff}.Button--color--good{background-color:#4d9121;color:#fff;transition:color .2s,background-color .2s}.Button--color--good:hover{background-color:#67b335;color:#fff}.Button--color--good--translucent{background-color:rgba(77,145,33,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--good--translucent:hover{background-color:rgba(103,179,53,.5);color:#fff}.Button--color--average{background-color:#cd7a0d;color:#fff;transition:color .2s,background-color .2s}.Button--color--average:hover{background-color:#eb972b;color:#fff}.Button--color--average--translucent{background-color:rgba(205,122,13,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--average--translucent:hover{background-color:rgba(235,151,43,.5);color:#fff}.Button--color--bad{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.Button--color--bad:hover{background-color:#d93f3f;color:#fff}.Button--color--bad--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--bad--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.Button--color--label{background-color:#657a94;color:#fff;transition:color .2s,background-color .2s}.Button--color--label:hover{background-color:#8a9aae;color:#fff}.Button--color--label--translucent{background-color:rgba(101,122,148,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--label--translucent:hover{background-color:rgba(138,154,174,.5);color:#fff}.Button--color--gold{background-color:#d6920c;color:#fff;transition:color .2s,background-color .2s}.Button--color--gold:hover{background-color:#eeaf30;color:#fff}.Button--color--gold--translucent{background-color:rgba(214,146,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--gold--translucent:hover{background-color:rgba(238,175,48,.5);color:#fff}.Button--color--transparent{background-color:rgba(32,32,32,0);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--transparent:hover{background-color:rgba(50,50,50,.81);color:#fff}.Button--color--default{background-color:#3e6189;color:#fff;transition:color .2s,background-color .2s}.Button--color--default:hover{background-color:#567daa;color:#fff}.Button--color--default--translucent{background-color:rgba(40,40,40,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--default--translucent:hover{background-color:rgba(61,61,61,.5);color:#fff}.Button--color--caution{background-color:#d9b804;color:#000;transition:color .2s,background-color .2s}.Button--color--caution:hover{background-color:#f5d523;color:#000}.Button--color--caution--translucent{background-color:rgba(217,184,4,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--caution--translucent:hover{background-color:rgba(245,213,35,.5);color:#fff}.Button--color--danger{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.Button--color--danger:hover{background-color:#d93f3f;color:#fff}.Button--color--danger--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.Button--color--danger--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.Button--disabled{background-color:#999!important;color:rgba(255,255,255,.75)!important}.Button--disabled--translucent{background-color:rgba(77,23,23,.5)!important;color:rgba(255,255,255,.5)!important}.Button--selected,.Button--selected--translucent{background-color:#1b9638;color:#fff;transition:color .2s,background-color .2s}.Button--selected:hover,.Button--selected--translucent:hover{background-color:#2fb94f;color:#fff}.ColorBox{display:inline-block;width:1em;height:1em;line-height:1em;text-align:center}.Dimmer{display:flex;justify-content:center;align-items:center;position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.75);z-index:1}.Dropdown{position:relative;align-items:center}.Dropdown__control{display:inline-block;align-items:center;font-family:Verdana,sans-serif;font-size:1em;width:8.3333333333em;line-height:1.3333333333em;-ms-user-select:none;user-select:none}.Dropdown__arrow-button{float:right;padding-left:.35em;width:1.2em;height:1.8333333333em;border-left:.0833333333em solid #000;border-left:.0833333333em solid rgba(0,0,0,.25)}.Dropdown__menu{overflow-y:auto;align-items:center;z-index:5;max-height:16.6666666667em;border-radius:0 0 .1666666667em .1666666667em;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75)}.Dropdown__menu-scroll{overflow-y:scroll}.Dropdown__menuentry{padding:.1666666667em .3333333333em;font-family:Verdana,sans-serif;font-size:1em;line-height:1.4166666667em;transition:background-color .1s ease-out}.Dropdown__menuentry.selected{background-color:rgba(255,255,255,.5)!important;transition:background-color 0ms}.Dropdown__menuentry:hover{background-color:rgba(255,255,255,.2);transition:background-color 0ms}.Dropdown__over{top:auto;bottom:100%}.Dropdown__selected-text{display:inline-block;text-overflow:ellipsis;white-space:nowrap;height:1.4166666667em;width:calc(100% - 1.2em);text-align:left;padding-top:2.5px}.Flex{display:-ms-flexbox;display:flex}.Flex--inline{display:inline-flex}.Flex--iefix{display:block}.Flex--iefix.Flex--inline,.Flex__item--iefix{display:inline-block}.Flex--iefix--column>.Flex__item--iefix{display:block}.Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.Knob__popupValue,.Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Knob__popupValue--right{top:.25rem;right:-50%}.Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.Knob__ringTrackPivot{transform:rotate(135deg)}.Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.Knob__ringFillPivot{transform:rotate(135deg)}.Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.Knob--color--white .Knob__ringFill{stroke:#fff}.Knob--color--red .Knob__ringFill{stroke:#df3e3e}.Knob--color--orange .Knob__ringFill{stroke:#f37f33}.Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.Knob--color--green .Knob__ringFill{stroke:#25ca4c}.Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.Knob--color--blue .Knob__ringFill{stroke:#2e93de}.Knob--color--violet .Knob__ringFill{stroke:#7349cf}.Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.Knob--color--pink .Knob__ringFill{stroke:#e34da1}.Knob--color--brown .Knob__ringFill{stroke:#b97447}.Knob--color--grey .Knob__ringFill{stroke:#848484}.Knob--color--good .Knob__ringFill{stroke:#68c22d}.Knob--color--average .Knob__ringFill{stroke:#f29a29}.Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.LabeledList{display:table;width:100%;width:calc(100% + 1em);border-collapse:collapse;border-spacing:0;margin:-.25em -.5em 0;padding:0}.LabeledList__row{display:table-row}.LabeledList__row:last-child .LabeledList__cell{padding-bottom:0}.LabeledList__cell{display:table-cell;margin:0;padding:.25em .5em;border:0;text-align:left;vertical-align:baseline}.LabeledList__label{width:1%;white-space:nowrap;min-width:5em}.LabeledList__buttons{width:.1%;white-space:nowrap;text-align:right;padding-top:.0833333333em;padding-bottom:0}.LabeledList__breakContents{word-break:break-all;word-wrap:break-word}.Modal{background-color:#202020;max-width:calc(100% - 1rem);padding:1rem;scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#000;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.NoticeBox--color--black{color:#fff;background-color:#000}.NoticeBox--color--white{color:#000;background-color:#b3b3b3}.NoticeBox--color--red{color:#fff;background-color:#701f1f}.NoticeBox--color--orange{color:#fff;background-color:#854114}.NoticeBox--color--yellow{color:#000;background-color:#83710d}.NoticeBox--color--olive{color:#000;background-color:#576015}.NoticeBox--color--green{color:#fff;background-color:#174e24}.NoticeBox--color--teal{color:#fff;background-color:#064845}.NoticeBox--color--blue{color:#fff;background-color:#1b4565}.NoticeBox--color--violet{color:#fff;background-color:#3b2864}.NoticeBox--color--purple{color:#fff;background-color:#542663}.NoticeBox--color--pink{color:#fff;background-color:#802257}.NoticeBox--color--brown{color:#fff;background-color:#4c3729}.NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.NoticeBox--color--average{color:#fff;background-color:#7b4e13}.NoticeBox--color--bad{color:#fff;background-color:#701f1f}.NoticeBox--color--label{color:#fff;background-color:#53565a}.NoticeBox--color--gold{color:#fff;background-color:#825d13}.NoticeBox--type--info{color:#fff;background-color:#235982}.NoticeBox--type--success{color:#fff;background-color:#1e662f}.NoticeBox--type--warning{color:#fff;background-color:#a95219}.NoticeBox--type--danger{color:#fff;background-color:#8f2828}.NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.NumberInput--fluid{display:block}.NumberInput__content{margin-left:.5em}.NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.ProgressBar__fill--animated{transition:background-color .5s,width .5s}.ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.ProgressBar--color--default{border:.0833333333em solid #3e6189}.ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.ProgressBar--color--disabled{border:1px solid #999}.ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.ProgressBar--color--black{border:.0833333333em solid #000!important}.ProgressBar--color--black .ProgressBar__fill{background-color:#000}.ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.ProgressBar--color--grey{border:.0833333333em solid #646464!important}.ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--label{border:.0833333333em solid #657a94!important}.ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.Section{position:relative;margin-bottom:.5em;background-color:#131313;box-sizing:border-box}.Section:last-child{margin-bottom:0}.Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.Section__rest{position:relative}.Section__content{padding:.66em .5em}.Section--fitted>.Section__rest>.Section__content{padding:0}.Section--fill{display:flex;flex-direction:column;height:100%}.Section--fill>.Section__rest{flex-grow:1}.Section--fill>.Section__rest>.Section__content{height:100%}.Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.Section--scrollable{overflow-x:hidden;overflow-y:hidden}.Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.Section .Section:first-child{margin-top:-.5em}.Section .Section .Section__titleText{font-size:1.0833333333em}.Section .Section .Section .Section__titleText{font-size:1em}.Slider:not(.Slider__disabled){cursor:e-resize}.Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Divider--horizontal{margin:.5em 0}.Divider--horizontal:not(.Divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Divider--vertical{height:100%;margin:0 .5em}.Divider--vertical:not(.Divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--fill{height:100%}.Stack--horizontal>.Stack__item{margin-left:.5em}.Stack--horizontal>.Stack__item:first-child{margin-left:0}.Stack--vertical>.Stack__item{margin-top:.5em}.Stack--vertical>.Stack__item:first-child{margin-top:0}.Stack--zebra>.Stack__item:nth-child(2n){background-color:#131313}.Stack--horizontal>.Stack__divider:not(.Stack__divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--vertical>.Stack__divider:not(.Stack__divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Table{display:table;width:100%;border-collapse:collapse;border-spacing:0;margin:0}.Table--collapsing{width:auto}.Table__row{display:table-row}.Table__cell{display:table-cell;padding:0 .25em}.Table__cell:first-child{padding-left:0}.Table__cell:last-child{padding-right:0}.Table__row--header .Table__cell,.Table__cell--header{font-weight:700;padding-bottom:.5em}.Table__cell--collapsing{width:1%;white-space:nowrap}.Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#131313}.Tabs--fill{height:100%}.Section .Tabs{background-color:rgba(0,0,0,0)}.Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.Tabs--horizontal:last-child{margin-bottom:0}.Tabs__Tab{flex-grow:0}.Tabs--fluid .Tabs__Tab{flex-grow:1}.Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(255,255,255,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.Tab:not(.Tab--selected):hover{background-color:rgba(255,255,255,.075);transition:background-color 0}.Tab--selected{background-color:rgba(255,255,255,.125);color:#dfe7f0}.Tab__text{flex-grow:1;margin:0 .5em}.Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #d4dfec}.Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.Tabs--vertical .Tab--selected{border-left:.1666666667em solid #d4dfec}.Tab--selected.Tab--color--black{color:#535353}.Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#1a1a1a}.Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#1a1a1a}.Tab--selected.Tab--color--white{color:#fff}.Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#fff}.Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#fff}.Tab--selected.Tab--color--red{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#df3e3e}.Tab--selected.Tab--color--orange{color:#f69f66}.Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#f37f33}.Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#f37f33}.Tab--selected.Tab--color--yellow{color:#fce358}.Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#fbda21}.Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#fbda21}.Tab--selected.Tab--color--olive{color:#d8eb55}.Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#cbe41c}.Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#cbe41c}.Tab--selected.Tab--color--green{color:#53e074}.Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#25ca4c}.Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#25ca4c}.Tab--selected.Tab--color--teal{color:#21fff5}.Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00d6cc}.Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00d6cc}.Tab--selected.Tab--color--blue{color:#62aee6}.Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#2e93de}.Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#2e93de}.Tab--selected.Tab--color--violet{color:#9676db}.Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#7349cf}.Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#7349cf}.Tab--selected.Tab--color--purple{color:#c274db}.Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#ad45d0}.Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#ad45d0}.Tab--selected.Tab--color--pink{color:#ea79b9}.Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#e34da1}.Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#e34da1}.Tab--selected.Tab--color--brown{color:#ca9775}.Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#b97447}.Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#b97447}.Tab--selected.Tab--color--grey{color:#a3a3a3}.Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#848484}.Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#848484}.Tab--selected.Tab--color--good{color:#8cd95a}.Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#68c22d}.Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#68c22d}.Tab--selected.Tab--color--average{color:#f5b35e}.Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#f29a29}.Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#f29a29}.Tab--selected.Tab--color--bad{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#df3e3e}.Tab--selected.Tab--color--label{color:#a8b4c4}.Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#8b9bb0}.Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#8b9bb0}.Tab--selected.Tab--color--gold{color:#f6c563}.Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#f3b22f}.Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#f3b22f}.Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.Input--fluid{display:block;width:auto}.Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input--monospace .Input__input{font-family:Consolas,monospace}.TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.TextArea--fluid{display:block;width:auto;height:auto}.TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.Tooltip{z-index:2;padding:.5em .75em;pointer-events:none;text-align:left;transition:opacity .15s ease-out;background-color:#000;color:#fff;box-shadow:.1em .1em 1.25em -.1em rgba(0,0,0,.5);border-radius:.16em;max-width:20.8333333333em}.Chat{color:#abc6ec}.Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.Chat__badge:before{content:"x"}.Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.Chat__scrollButton{position:fixed;right:2em;bottom:1em}.Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#131313}.Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.Chat__highlight{color:#000}.Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.ChatMessage{word-wrap:break-word}.ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.Ping{position:relative;padding:.125em .25em;border:.0833333333em solid rgba(140,140,140,.5);border-radius:.25em;width:3.75em;text-align:right}.Ping__indicator{content:"";position:absolute;top:.5em;left:.5em;width:.5em;height:.5em;background-color:#888;border-radius:.25em}.Notifications{position:absolute;top:1em;left:.75em;right:2em}.Notification{color:#fff;background-color:#dc143c;padding:.5em;margin:1em 0}.Notification:first-child{margin-top:0}.Notification:last-child{margin-bottom:0}html,body{scrollbar-color:#363636 #181818}.Layout,.Layout *{scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.Layout__content--flexRow{display:flex;flex-flow:row}.Layout__content--flexColumn{display:flex;flex-flow:column}.Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.Layout__content--noMargin{margin:0}.Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#202020;background-image:linear-gradient(to bottom,#202020,#202020)}.Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.Window__contentPadding:after{height:0}.Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(56,56,56,.25);pointer-events:none}.Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.emoji16x16{vertical-align:middle}a{color:#397ea5}a.popt{text-decoration:none}.popup{position:fixed;top:50%;left:50%;background:#ddd}.popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.popup .close:hover{background:#999}.popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.popup input[type=text]:hover,.popup input[type=text]:active,.popup input[type=text]:focus{border-color:green}.popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.popup input[type=submit]:hover,.popup input[type=submit]:focus,.popup input[type=submit]:active{background:#aaa;cursor:pointer}.changeFont{padding:10px}.changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.changeFont a:hover{background:#ccc}.highlightPopup{padding:10px;text-align:center}.highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.highlightPopup input.highlightColor{background-color:#ff0}.highlightPopup input.highlightTermSubmit{margin-top:5px}.contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.contextMenu a:hover{background-color:#ccc}.filterMessages{padding:5px}.filterMessages div{padding:2px 0}.icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.motd{color:#a4bad6;font-family:Verdana,sans-serif;white-space:normal}.motd h1,.motd h2,.motd h3,.motd h4,.motd h5,.motd h6{color:#a4bad6;text-decoration:underline}.motd a,.motd a:link,.motd a:active,.motd a:hover{color:#a4bad6}.italic,.italics,.emote{font-style:italic}.highlight{background:#ff0}h1,h2,h3,h4,h5,h6{color:#a4bad6;font-family:Georgia,Verdana,sans-serif}em{font-style:normal;font-weight:700}.darkmblue{color:#6685f5}.prefix,.ooc{font-weight:700}.looc{color:#69c;font-weight:700}.adminobserverooc{color:#09c;font-weight:700}.adminooc{color:#b82e00;font-weight:700}.adminobserver{color:#960;font-weight:700}.admin{color:#386aff;font-weight:700}.adminsay{color:#9611d4;font-weight:700}.mentorhelp{color:#07b;font-weight:700}.adminhelp{color:#a00;font-weight:700}.playerreply{color:#80b;font-weight:700}.pmsend{color:#6685f5}.debug{color:#6d2f83}.name,.yell{font-weight:700}.siliconsay{font-family:Courier New,Courier,monospace}.deadsay{color:#e2c1ff}.radio{color:#20b142}.deptradio{color:#939}.comradio{color:#5f5cff}.syndradio{color:#8f4a4b}.dsquadradio{color:#998599}.resteamradio{color:#18bc46}.airadio{color:#ff5ed7}.centradio{color:#2681a5}.secradio{color:#dd3535}.engradio{color:#feac20}.medradio{color:#00b5ad}.sciradio{color:#c68cfa}.supradio{color:#b88646}.srvradio{color:#bbd164}.proradio{color:#b84f92}.admin_channel{color:#03fc9d;font-weight:700}.all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.mentor_channel{color:#775bff;font-weight:700}.mentor_channel_admin{color:#a35cff;font-weight:700}.dev_channel{color:#775bff;font-weight:700}.dev_channel_admin{color:#a35cff;font-weight:700}.djradio{color:#960}.binaryradio{color:#1b00fb;font-family:Courier New,Courier,monospace}.mommiradio{color:#6685f5}.alert{color:#d82020}h1.alert,h2.alert{color:#a4bad6}.ghostalert{color:#cc00c6;font-style:italic;font-weight:700}.emote{font-style:italic}.selecteddna{color:#a4bad6;background-color:#001b1b}.attack{color:red}.moderate{color:#c00}.disarm{color:#900}.passive{color:#600}.warning{color:#c51e1e;font-style:italic}.boldwarning{color:#c51e1e;font-style:italic;font-weight:700}.danger{color:#c51e1e;font-weight:700}.userdanger{color:#c51e1e;font-weight:700;font-size:120%}.biggerdanger{color:red;font-weight:700;font-size:150%}.notice{color:#6685f5}.boldnotice{color:#6685f5;font-weight:700}.suicide{color:#ff5050;font-style:italic}.green{color:#03bb39}.pr_announce,.boldannounceic,.boldannounceooc{color:#c51e1e;font-weight:700}.greenannounce{color:#059223;font-weight:700}.alien{color:#c433c4}.noticealien{color:#00c000}.alertalien{color:#00c000;font-weight:700}.terrorspider{color:#cf52fa}.dantalion{color:#8b2c5e}.chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.chaosgood{color:#19e0c0;font-weight:700}.chaosneutral{color:#479ac0;font-weight:700}.chaosbad{color:#9047c0;font-weight:700}.chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.sinister{color:purple;font-weight:700;font-style:italic}.medal{font-weight:700}.blob{color:#006221;font-weight:700;font-style:italic}.confirm{color:#00af3b}.rose{color:#ff5050}.sans{font-family:Comic Sans MS,cursive,sans-serif}.wingdings{font-family:Wingdings,Webdings}.robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.ancient{color:#008b8b;font-style:italic}.newscaster{color:#c00}.mod{color:#735638;font-weight:700}.modooc{color:#184880;font-weight:700}.adminmod{color:#f0aa14;font-weight:700}.tajaran{color:#803b56}.skrell{color:#00ced1}.solcom{color:#8282fb}.com_srus{color:#7c4848}.soghun{color:#228b22}.changeling{color:#00b4de}.vox{color:#a0a}.diona{color:#804000;font-weight:700}.trinary{color:#727272}.kidan{color:#c64c05}.slime{color:#07a}.drask{color:#a3d4eb;font-family:Arial Black}.moth{color:#869b29;font-family:Copperplate}.clown{color:red}.vulpkanin{color:#b97a57}.abductor{color:purple;font-style:italic}.mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.rough{font-family:Trebuchet MS,cursive,sans-serif}.say_quote{font-family:Georgia,Verdana,sans-serif}.cult{color:purple;font-weight:700;font-style:italic}.cultspeech{color:#af0000;font-style:italic}.cultitalic{color:#a60000;font-style:italic}.cultlarge{color:#a60000;font-weight:700;font-size:120%}.narsie{color:#a60000;font-weight:700;font-size:300%}.narsiesmall{color:#a60000;font-weight:700;font-size:200%}.zombie{color:#7c4848}.zombielarge{color:#7c4848;font-weight:700;font-size:120%}.interface{color:#9031c4}.big{font-size:150%}.reallybig{font-size:175%}.greentext{color:#0f0;font-size:150%}.redtext{color:red;font-size:150%}.bold{font-weight:700}.his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.center{text-align:center}.red{color:red}.purple{color:#9031c4}.skeleton{color:#c8c8c8;font-weight:700;font-style:italic}.gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.orange{color:orange}.orangei{color:orange;font-style:italic}.orangeb{color:orange;font-weight:700}.resonate{color:#298f85}.healthscan_oxy{color:#5cc9ff}.revennotice{color:#6685f5}.revenboldnotice{color:#6685f5;font-weight:700}.revenbignotice{color:#6685f5;font-weight:700;font-size:120%}.revenminor{color:#823abb}.revenwarning{color:#760fbb;font-style:italic}.revendanger{color:#760fbb;font-weight:700;font-size:120%}.specialnotice{color:#4a6f82;font-weight:700;font-size:120%}.good{color:green}.average{color:#ff8000}.bad{color:red}.italics,.talkinto{font-style:italic}.whisper{font-style:italic;color:#ccc}.recruit{color:#5c00e6;font-weight:700;font-style:italic}.memo{color:#638500;text-align:center}.memoedit{text-align:center;font-size:75%}.connectionClosed,.fatalError{background:red;color:#fff;padding:5px}.connectionClosed.restored{background:green}.internal.boldnshit{color:#6685f5;font-weight:700}.rebooting{background:#2979af;color:#fff;padding:5px}.rebooting a{color:#fff!important;text-decoration-color:#fff!important}.text-normal{font-weight:400;font-style:normal}.hidden{display:none;visibility:hidden}.colossus{color:#7f282a;font-size:175%}.hierophant{color:#609;font-weight:700;font-style:italic}.hierophant_warning{color:#609;font-style:italic}.emoji{max-height:16px;max-width:16px}.adminticket{color:#3daf21;font-weight:700}.adminticketalt{color:#ccb847;font-weight:700}span.body .codephrases{color:#55f}span.body .coderesponses{color:#f33}.announcement h1,.announcement h2{color:#a4bad6;margin:8pt 0;line-height:1.2}.announcement p{color:#d82020;line-height:1.3}.announcement.minor h1{font-size:180%}.announcement.minor h2{font-size:170%}.announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.bolditalics{font-style:italic;font-weight:700}.boxed_message{background:#1b1c1e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.boxed_message.left_align_text{text-align:left}.boxed_message.red_border{background:#1e1b1b;border-color:#a00}.boxed_message.green_border{background:#1b1e1c;border-color:#0f0}.boxed_message.purple_border{background:#1d1c1f;border-color:#8000ff}.boxed_message.notice_border{background:#1b1c1e;border-color:#6685f5}.boxed_message.thick_border{border-width:thick}.oxygen{color:#449dff}.nitrogen{color:#f94541}.carbon_dioxide{color:#ccc}.plasma{color:#eb6b00}.sleeping_agent{color:#f28b89}.agent_b{color:teal}.theme-light .color-black{color:#000!important}.theme-light .color-white{color:#e6e6e6!important}.theme-light .color-red{color:#c82121!important}.theme-light .color-orange{color:#e6630d!important}.theme-light .color-yellow{color:#e5c304!important}.theme-light .color-olive{color:#a3b816!important}.theme-light .color-green{color:#1d9f3b!important}.theme-light .color-teal{color:#00a39c!important}.theme-light .color-blue{color:#1e78bb!important}.theme-light .color-violet{color:#5a30b5!important}.theme-light .color-purple{color:#932eb4!important}.theme-light .color-pink{color:#db228a!important}.theme-light .color-brown{color:#955d39!important}.theme-light .color-grey{color:#e6e6e6!important}.theme-light .color-good{color:#529923!important}.theme-light .color-average{color:#da810e!important}.theme-light .color-bad{color:#c82121!important}.theme-light .color-label{color:#353535!important}.theme-light .color-gold{color:#e39b0d!important}.theme-light .color-bg-black{background-color:#000!important}.theme-light .color-bg-white{background-color:#bfbfbf!important}.theme-light .color-bg-red{background-color:#a61c1c!important}.theme-light .color-bg-orange{background-color:#c0530b!important}.theme-light .color-bg-yellow{background-color:#bfa303!important}.theme-light .color-bg-olive{background-color:#889912!important}.theme-light .color-bg-green{background-color:#188532!important}.theme-light .color-bg-teal{background-color:#008882!important}.theme-light .color-bg-blue{background-color:#19649c!important}.theme-light .color-bg-violet{background-color:#4b2897!important}.theme-light .color-bg-purple{background-color:#7a2696!important}.theme-light .color-bg-pink{background-color:#b61d73!important}.theme-light .color-bg-brown{background-color:#7c4d2f!important}.theme-light .color-bg-grey{background-color:#bfbfbf!important}.theme-light .color-bg-good{background-color:#44801d!important}.theme-light .color-bg-average{background-color:#b56b0b!important}.theme-light .color-bg-bad{background-color:#a61c1c!important}.theme-light .color-bg-label{background-color:#2c2c2c!important}.theme-light .color-bg-gold{background-color:#bd810b!important}.theme-light .color-border-black{border-color:#000!important}.theme-light .color-border-white{border-color:#e6e6e6!important}.theme-light .color-border-red{border-color:#c82121!important}.theme-light .color-border-orange{border-color:#e6630d!important}.theme-light .color-border-yellow{border-color:#e5c304!important}.theme-light .color-border-olive{border-color:#a3b816!important}.theme-light .color-border-green{border-color:#1d9f3b!important}.theme-light .color-border-teal{border-color:#00a39c!important}.theme-light .color-border-blue{border-color:#1e78bb!important}.theme-light .color-border-violet{border-color:#5a30b5!important}.theme-light .color-border-purple{border-color:#932eb4!important}.theme-light .color-border-pink{border-color:#db228a!important}.theme-light .color-border-brown{border-color:#955d39!important}.theme-light .color-border-grey{border-color:#e6e6e6!important}.theme-light .color-border-good{border-color:#529923!important}.theme-light .color-border-average{border-color:#da810e!important}.theme-light .color-border-bad{border-color:#c82121!important}.theme-light .color-border-label{border-color:#353535!important}.theme-light .color-border-gold{border-color:#e39b0d!important}.theme-light .Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#fff}.theme-light .Tabs--fill{height:100%}.theme-light .Section .Tabs{background-color:rgba(0,0,0,0)}.theme-light .Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.theme-light .Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.theme-light .Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.theme-light .Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.theme-light .Tabs--horizontal:last-child{margin-bottom:0}.theme-light .Tabs__Tab{flex-grow:0}.theme-light .Tabs--fluid .Tabs__Tab{flex-grow:1}.theme-light .Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(0,0,0,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.theme-light .Tab:not(.Tab--selected):hover{background-color:rgba(0,0,0,.075);transition:background-color 0}.theme-light .Tab--selected{background-color:rgba(0,0,0,.125);color:#404040}.theme-light .Tab__text{flex-grow:1;margin:0 .5em}.theme-light .Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.theme-light .Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.theme-light .Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.theme-light .Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #000}.theme-light .Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.theme-light .Tabs--vertical .Tab--selected{border-left:.1666666667em solid #000}.theme-light .Tab--selected.Tab--color--black{color:#404040}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#000}.theme-light .Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#000}.theme-light .Tab--selected.Tab--color--white{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--red{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--orange{color:#f48942}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#e6630d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#e6630d}.theme-light .Tab--selected.Tab--color--yellow{color:#fcdd33}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#e5c304}.theme-light .Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#e5c304}.theme-light .Tab--selected.Tab--color--olive{color:#d0e732}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#a3b816}.theme-light .Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#a3b816}.theme-light .Tab--selected.Tab--color--green{color:#33da5a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#1d9f3b}.theme-light .Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#1d9f3b}.theme-light .Tab--selected.Tab--color--teal{color:#00faef}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00a39c}.theme-light .Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00a39c}.theme-light .Tab--selected.Tab--color--blue{color:#419ce1}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#1e78bb}.theme-light .Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#1e78bb}.theme-light .Tab--selected.Tab--color--violet{color:#7f58d3}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#5a30b5}.theme-light .Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#5a30b5}.theme-light .Tab--selected.Tab--color--purple{color:#b455d4}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#932eb4}.theme-light .Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#932eb4}.theme-light .Tab--selected.Tab--color--pink{color:#e558a7}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#db228a}.theme-light .Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#db228a}.theme-light .Tab--selected.Tab--color--brown{color:#c0825a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#955d39}.theme-light .Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#955d39}.theme-light .Tab--selected.Tab--color--grey{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--good{color:#77d23b}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#529923}.theme-light .Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#529923}.theme-light .Tab--selected.Tab--color--average{color:#f3a23a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#da810e}.theme-light .Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#da810e}.theme-light .Tab--selected.Tab--color--bad{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--label{color:#686868}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#353535}.theme-light .Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#353535}.theme-light .Tab--selected.Tab--color--gold{color:#f4b73f}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#e39b0d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#e39b0d}.theme-light .Section{position:relative;margin-bottom:.5em;background-color:#fff;box-sizing:border-box}.theme-light .Section:last-child{margin-bottom:0}.theme-light .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #fff}.theme-light .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#000}.theme-light .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-light .Section__rest{position:relative}.theme-light .Section__content{padding:.66em .5em}.theme-light .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-light .Section--fill{display:flex;flex-direction:column;height:100%}.theme-light .Section--fill>.Section__rest{flex-grow:1}.theme-light .Section--fill>.Section__rest>.Section__content{height:100%}.theme-light .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-light .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-light .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-light .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-light .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-light .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-light .Section .Section:first-child{margin-top:-.5em}.theme-light .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-light .Section .Section .Section .Section__titleText{font-size:1em}.theme-light .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-light .Button:last-child{margin-right:0;margin-bottom:0}.theme-light .Button .fa,.theme-light .Button .fas,.theme-light .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-light .Button--hasContent .fa,.theme-light .Button--hasContent .fas,.theme-light .Button--hasContent .far{margin-right:.25em}.theme-light .Button--hasContent.Button--iconRight .fa,.theme-light .Button--hasContent.Button--iconRight .fas,.theme-light .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-light .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-light .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-light .Button--circular{border-radius:50%}.theme-light .Button--compact{padding:0 .25em;line-height:1.333em}.theme-light .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-light .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-light .Button--color--black{background-color:#000;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--black:hover{background-color:#101010;color:#fff}.theme-light .Button--color--black--translucent{background-color:rgba(0,0,0,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--black--translucent:hover{background-color:rgba(16,16,16,.5);color:#fff}.theme-light .Button--color--white{background-color:#bfbfbf;color:#000;transition:color .2s,background-color .2s}.theme-light .Button--color--white:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--white--translucent{background-color:rgba(191,191,191,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--white--translucent:hover{background-color:rgba(231,231,231,.5);color:#fff}.theme-light .Button--color--red{background-color:#a61c1c;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--red:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--red--translucent{background-color:rgba(166,28,28,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--red--translucent:hover{background-color:rgba(203,48,48,.5);color:#fff}.theme-light .Button--color--orange{background-color:#c0530b;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--orange:hover{background-color:#e76d1d;color:#fff}.theme-light .Button--color--orange--translucent{background-color:rgba(192,83,11,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--orange--translucent:hover{background-color:rgba(231,109,29,.5);color:#fff}.theme-light .Button--color--yellow{background-color:#bfa303;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--yellow:hover{background-color:#e7c714;color:#fff}.theme-light .Button--color--yellow--translucent{background-color:rgba(191,163,3,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--yellow--translucent:hover{background-color:rgba(231,199,20,.5);color:#fff}.theme-light .Button--color--olive{background-color:#889912;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--olive:hover{background-color:#a9bc25;color:#fff}.theme-light .Button--color--olive--translucent{background-color:rgba(136,153,18,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--olive--translucent:hover{background-color:rgba(169,188,37,.5);color:#fff}.theme-light .Button--color--green{background-color:#188532;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--green:hover{background-color:#2ba648;color:#fff}.theme-light .Button--color--green--translucent{background-color:rgba(24,133,50,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--green--translucent:hover{background-color:rgba(43,166,72,.5);color:#fff}.theme-light .Button--color--teal{background-color:#008882;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--teal:hover{background-color:#10a9a2;color:#fff}.theme-light .Button--color--teal--translucent{background-color:rgba(0,136,130,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--teal--translucent:hover{background-color:rgba(16,169,162,.5);color:#fff}.theme-light .Button--color--blue{background-color:#19649c;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--blue:hover{background-color:#2c81c0;color:#fff}.theme-light .Button--color--blue--translucent{background-color:rgba(25,100,156,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--blue--translucent:hover{background-color:rgba(44,129,192,.5);color:#fff}.theme-light .Button--color--violet{background-color:#4b2897;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--violet:hover{background-color:#653db9;color:#fff}.theme-light .Button--color--violet--translucent{background-color:rgba(75,40,151,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--violet--translucent:hover{background-color:rgba(101,61,185,.5);color:#fff}.theme-light .Button--color--purple{background-color:#7a2696;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--purple:hover{background-color:#9a3bb9;color:#fff}.theme-light .Button--color--purple--translucent{background-color:rgba(122,38,150,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--purple--translucent:hover{background-color:rgba(154,59,185,.5);color:#fff}.theme-light .Button--color--pink{background-color:#b61d73;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--pink:hover{background-color:#d93591;color:#fff}.theme-light .Button--color--pink--translucent{background-color:rgba(182,29,115,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--pink--translucent:hover{background-color:rgba(217,53,145,.5);color:#fff}.theme-light .Button--color--brown{background-color:#7c4d2f;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--brown:hover{background-color:#9c6745;color:#fff}.theme-light .Button--color--brown--translucent{background-color:rgba(124,77,47,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--brown--translucent:hover{background-color:rgba(156,103,69,.5);color:#fff}.theme-light .Button--color--grey{background-color:#bfbfbf;color:#000;transition:color .2s,background-color .2s}.theme-light .Button--color--grey:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--grey--translucent{background-color:rgba(191,191,191,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--grey--translucent:hover{background-color:rgba(231,231,231,.5);color:#fff}.theme-light .Button--color--good{background-color:#44801d;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--good:hover{background-color:#5d9f31;color:#fff}.theme-light .Button--color--good--translucent{background-color:rgba(68,128,29,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--good--translucent:hover{background-color:rgba(93,159,49,.5);color:#fff}.theme-light .Button--color--average{background-color:#b56b0b;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--average:hover{background-color:#dc891d;color:#fff}.theme-light .Button--color--average--translucent{background-color:rgba(181,107,11,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--average--translucent:hover{background-color:rgba(220,137,29,.5);color:#fff}.theme-light .Button--color--bad{background-color:#a61c1c;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--bad:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--bad--translucent{background-color:rgba(166,28,28,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--bad--translucent:hover{background-color:rgba(203,48,48,.5);color:#fff}.theme-light .Button--color--label{background-color:#2c2c2c;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--label:hover{background-color:#424242;color:#fff}.theme-light .Button--color--label--translucent{background-color:rgba(44,44,44,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--label--translucent:hover{background-color:rgba(66,66,66,.5);color:#fff}.theme-light .Button--color--gold{background-color:#bd810b;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--gold:hover{background-color:#e5a11c;color:#fff}.theme-light .Button--color--gold--translucent{background-color:rgba(189,129,11,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--gold--translucent:hover{background-color:rgba(229,161,28,.5);color:#fff}.theme-light .Button--color--transparent{background-color:rgba(238,238,238,0);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--transparent:hover{background-color:rgba(255,255,255,.81);color:#000}.theme-light .Button--color--default{background-color:#bbb;color:#000;transition:color .2s,background-color .2s}.theme-light .Button--color--default:hover{background-color:#e3e3e3;color:#000}.theme-light .Button--color--default--translucent{background-color:rgba(251,251,251,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--default--translucent:hover{background-color:rgba(254,254,254,.5);color:#fff}.theme-light .Button--color--caution{background-color:#be6209;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-light .Button--color--caution--translucent{background-color:rgba(190,98,9,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--caution--translucent:hover{background-color:rgba(230,127,26,.5);color:#fff}.theme-light .Button--color--danger{background-color:#9a9d00;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-light .Button--color--danger--translucent{background-color:rgba(154,157,0,.33);color:rgba(0,0,0,.5);transition:color .2s,background-color .2s}.theme-light .Button--color--danger--translucent:hover{background-color:rgba(190,193,16,.5);color:#fff}.theme-light .Button--disabled{background-color:#363636!important;color:rgba(0,0,0,.75)!important}.theme-light .Button--disabled--translucent{background-color:rgba(77,23,23,.5)!important;color:rgba(0,0,0,.5)!important}.theme-light .Button--selected,.theme-light .Button--selected--translucent{background-color:#0668b8;color:#fff;transition:color .2s,background-color .2s}.theme-light .Button--selected:hover,.theme-light .Button--selected--translucent:hover{background-color:#1785df;color:#fff}.theme-light .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#353535;background-color:#e6e6e6;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-light .NumberInput--fluid{display:block}.theme-light .NumberInput__content{margin-left:.5em}.theme-light .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-light .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #353535;background-color:#353535}.theme-light .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#e6e6e6;color:#000;text-align:right}.theme-light .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#000;background-color:#e6e6e6;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-light .Input--disabled{color:#777;border-color:#000;border-color:rgba(0,0,0,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-light .Input--fluid{display:block;width:auto}.theme-light .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-light .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#000;color:inherit}.theme-light .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-light .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-light .TextArea{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;background-color:#e6e6e6;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-light .TextArea--fluid{display:block;width:auto;height:auto}.theme-light .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-light .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-light .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-light .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-light .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-light .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-light .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-light .Knob__popupValue,.theme-light .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .Knob__popupValue--right{top:.25rem;right:-50%}.theme-light .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-light .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-light .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-light .Knob__ringFillPivot{transform:rotate(135deg)}.theme-light .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-light .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-light .Knob--color--black .Knob__ringFill{stroke:#000}.theme-light .Knob--color--white .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--red .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--orange .Knob__ringFill{stroke:#e6630d}.theme-light .Knob--color--yellow .Knob__ringFill{stroke:#e5c304}.theme-light .Knob--color--olive .Knob__ringFill{stroke:#a3b816}.theme-light .Knob--color--green .Knob__ringFill{stroke:#1d9f3b}.theme-light .Knob--color--teal .Knob__ringFill{stroke:#00a39c}.theme-light .Knob--color--blue .Knob__ringFill{stroke:#1e78bb}.theme-light .Knob--color--violet .Knob__ringFill{stroke:#5a30b5}.theme-light .Knob--color--purple .Knob__ringFill{stroke:#932eb4}.theme-light .Knob--color--pink .Knob__ringFill{stroke:#db228a}.theme-light .Knob--color--brown .Knob__ringFill{stroke:#955d39}.theme-light .Knob--color--grey .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--good .Knob__ringFill{stroke:#529923}.theme-light .Knob--color--average .Knob__ringFill{stroke:#da810e}.theme-light .Knob--color--bad .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--label .Knob__ringFill{stroke:#353535}.theme-light .Knob--color--gold .Knob__ringFill{stroke:#e39b0d}.theme-light .Slider:not(.Slider__disabled){cursor:e-resize}.theme-light .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-light .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #000}.theme-light .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #000}.theme-light .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-light .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-light .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-light .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-light .ProgressBar--color--default{border:.0833333333em solid #bfbfbf}.theme-light .ProgressBar--color--default .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--disabled{border:1px solid #999}.theme-light .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-light .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-light .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-light .ProgressBar--color--white{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--white .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--red{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--red .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--orange{border:.0833333333em solid #c0530b!important}.theme-light .ProgressBar--color--orange .ProgressBar__fill{background-color:#c0530b}.theme-light .ProgressBar--color--yellow{border:.0833333333em solid #bfa303!important}.theme-light .ProgressBar--color--yellow .ProgressBar__fill{background-color:#bfa303}.theme-light .ProgressBar--color--olive{border:.0833333333em solid #889912!important}.theme-light .ProgressBar--color--olive .ProgressBar__fill{background-color:#889912}.theme-light .ProgressBar--color--green{border:.0833333333em solid #188532!important}.theme-light .ProgressBar--color--green .ProgressBar__fill{background-color:#188532}.theme-light .ProgressBar--color--teal{border:.0833333333em solid #008882!important}.theme-light .ProgressBar--color--teal .ProgressBar__fill{background-color:#008882}.theme-light .ProgressBar--color--blue{border:.0833333333em solid #19649c!important}.theme-light .ProgressBar--color--blue .ProgressBar__fill{background-color:#19649c}.theme-light .ProgressBar--color--violet{border:.0833333333em solid #4b2897!important}.theme-light .ProgressBar--color--violet .ProgressBar__fill{background-color:#4b2897}.theme-light .ProgressBar--color--purple{border:.0833333333em solid #7a2696!important}.theme-light .ProgressBar--color--purple .ProgressBar__fill{background-color:#7a2696}.theme-light .ProgressBar--color--pink{border:.0833333333em solid #b61d73!important}.theme-light .ProgressBar--color--pink .ProgressBar__fill{background-color:#b61d73}.theme-light .ProgressBar--color--brown{border:.0833333333em solid #7c4d2f!important}.theme-light .ProgressBar--color--brown .ProgressBar__fill{background-color:#7c4d2f}.theme-light .ProgressBar--color--grey{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--grey .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--good{border:.0833333333em solid #44801d!important}.theme-light .ProgressBar--color--good .ProgressBar__fill{background-color:#44801d}.theme-light .ProgressBar--color--average{border:.0833333333em solid #b56b0b!important}.theme-light .ProgressBar--color--average .ProgressBar__fill{background-color:#b56b0b}.theme-light .ProgressBar--color--bad{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--bad .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--label{border:.0833333333em solid #2c2c2c!important}.theme-light .ProgressBar--color--label .ProgressBar__fill{background-color:#2c2c2c}.theme-light .ProgressBar--color--gold{border:.0833333333em solid #bd810b!important}.theme-light .ProgressBar--color--gold .ProgressBar__fill{background-color:#bd810b}.theme-light .Chat{color:#000}.theme-light .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-light .Chat__badge:before{content:"x"}.theme-light .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-light .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-light .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-light .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#fff}.theme-light .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-light .Chat__highlight{color:#000}.theme-light .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-light .ChatMessage{word-wrap:break-word}.theme-light .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-light .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-light html,.theme-light body{scrollbar-color:#a7a7a7 #f2f2f2}.theme-light .Layout,.theme-light .Layout *{scrollbar-base-color:#f2f2f2;scrollbar-face-color:#d6d6d6;scrollbar-3dlight-color:#eee;scrollbar-highlight-color:#eee;scrollbar-track-color:#f2f2f2;scrollbar-arrow-color:#777;scrollbar-shadow-color:#d6d6d6}.theme-light .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-light .Layout__content--flexRow{display:flex;flex-flow:row}.theme-light .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-light .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-light .Layout__content--noMargin{margin:0}.theme-light .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#000;background-color:#eee;background-image:linear-gradient(to bottom,#eee,#eee)}.theme-light .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-light .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-light .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-light .Window__contentPadding:after{height:0}.theme-light .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-light .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(252,252,252,.25);pointer-events:none}.theme-light .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-light .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-light .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-light .TitleBar{background-color:#eee;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-light .TitleBar__clickable{color:rgba(0,0,0,.5);background-color:#eee;transition:color .25s,background-color .25s}.theme-light .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-light .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(0,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-light .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-light .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-light .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-light .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-light html,.theme-light body{padding:0;margin:0;height:100%;color:#000}.theme-light body{background:#fff;font-family:Verdana,sans-serif;font-size:13px;line-height:1.2;overflow-x:hidden;overflow-y:scroll;word-wrap:break-word}.theme-light img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}.theme-light img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.theme-light a{color:#00f}.theme-light a.popt{text-decoration:none}.theme-light .popup{position:fixed;top:50%;left:50%;background:#ddd}.theme-light .popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.theme-light .popup .close:hover{background:#999}.theme-light .popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.theme-light .popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.theme-light .popup input[type=text]:hover,.theme-light .popup input[type=text]:active,.theme-light .popup input[type=text]:focus{border-color:green}.theme-light .popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.theme-light .popup input[type=submit]:hover,.theme-light .popup input[type=submit]:focus,.theme-light .popup input[type=submit]:active{background:#aaa;cursor:pointer}.theme-light .changeFont{padding:10px}.theme-light .changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.theme-light .changeFont a:hover{background:#ccc}.theme-light .highlightPopup{padding:10px;text-align:center}.theme-light .highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.theme-light .highlightPopup input.highlightColor{background-color:#ff0}.theme-light .highlightPopup input.highlightTermSubmit{margin-top:5px}.theme-light .contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.theme-light .contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.theme-light .contextMenu a:hover{background-color:#ccc}.theme-light .filterMessages{padding:5px}.theme-light .filterMessages div{padding:2px 0}.theme-light .icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.theme-light .motd{color:#638500;font-family:Verdana,sans-serif;white-space:normal}.theme-light .motd h1,.theme-light .motd h2,.theme-light .motd h3,.theme-light .motd h4,.theme-light .motd h5,.theme-light .motd h6{color:#638500;text-decoration:underline}.theme-light .motd a,.theme-light .motd a:link,.theme-light .motd a:active,.theme-light .motd a:hover{color:#638500}.theme-light .italic,.theme-light .italics,.theme-light .emote{font-style:italic}.theme-light .highlight{background:#ff0}.theme-light h1,.theme-light h2,.theme-light h3,.theme-light h4,.theme-light h5,.theme-light h6{color:#00f;font-family:Georgia,Verdana,sans-serif}.theme-light em{font-style:normal;font-weight:700}.theme-light .darkmblue{color:#00f}.theme-light .prefix,.theme-light .ooc{font-weight:700}.theme-light .looc{color:#69c;font-weight:700}.theme-light .adminobserverooc{color:#09c;font-weight:700}.theme-light .adminooc{color:#b82e00;font-weight:700}.theme-light .adminobserver{color:#960;font-weight:700}.theme-light .admin{color:#386aff;font-weight:700}.theme-light .adminsay{color:#9611d4;font-weight:700}.theme-light .mentorhelp{color:#07b;font-weight:700}.theme-light .adminhelp{color:#a00;font-weight:700}.theme-light .playerreply{color:#80b;font-weight:700}.theme-light .pmsend{color:#00f}.theme-light .debug{color:#6d2f83}.theme-light .name,.theme-light .yell{font-weight:700}.theme-light .siliconsay{font-family:Courier New,Courier,monospace}.theme-light .deadsay{color:#5c00e6}.theme-light .radio{color:#408010}.theme-light .deptradio{color:#939}.theme-light .comradio{color:#204090}.theme-light .syndradio{color:#6d3f40}.theme-light .dsquadradio{color:#686868}.theme-light .resteamradio{color:#18bc46}.theme-light .airadio{color:#f0f}.theme-light .centradio{color:#5c5c7c}.theme-light .secradio{color:#a30000}.theme-light .engradio{color:#a66300}.theme-light .medradio{color:#009190}.theme-light .sciradio{color:#939}.theme-light .supradio{color:#7f6539}.theme-light .srvradio{color:#80a000}.theme-light .proradio{color:#e3027a}.theme-light .admin_channel{color:#9a04d1;font-weight:700}.theme-light .all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.theme-light .mentor_channel{color:#775bff;font-weight:700}.theme-light .mentor_channel_admin{color:#a35cff;font-weight:700}.theme-light .dev_channel{color:#775bff;font-weight:700}.theme-light .dev_channel_admin{color:#a35cff;font-weight:700}.theme-light .djradio{color:#630}.theme-light .binaryradio{color:#0b0050;font-family:Courier New,Courier,monospace}.theme-light .mommiradio{color:navy}.theme-light .alert{color:red}.theme-light h1.alert,.theme-light h2.alert{color:#000}.theme-light .ghostalert{color:#5c00e6;font-style:italic;font-weight:700}.theme-light .emote{font-style:italic}.theme-light .selecteddna{color:#fff;background-color:#001b1b}.theme-light .attack{color:red}.theme-light .moderate{color:#c00}.theme-light .disarm{color:#900}.theme-light .passive{color:#600}.theme-light .warning{color:red;font-style:italic}.theme-light .boldwarning{color:red;font-style:italic;font-weight:700}.theme-light .danger{color:red;font-weight:700}.theme-light .userdanger{color:red;font-weight:700;font-size:120%}.theme-light .biggerdanger{color:red;font-weight:700;font-size:150%}.theme-light .notice{color:#009}.theme-light .boldnotice{color:#009;font-weight:700}.theme-light .suicide{color:#ff5050;font-style:italic}.theme-light .green{color:#03bb39}.theme-light .pr_announce{color:#228b22;font-weight:700}.theme-light .boldannounceic,.theme-light .boldannounceooc{color:red;font-weight:700}.theme-light .greenannounce{color:#0f0;font-weight:700}.theme-light .alien{color:#543354}.theme-light .noticealien{color:#00c000}.theme-light .alertalien{color:#00c000;font-weight:700}.theme-light .terrorspider{color:#320e32}.theme-light .dantalion{color:#6a2148}.theme-light .chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.theme-light .chaosgood{color:#19e0c0;font-weight:700}.theme-light .chaosneutral{color:#479ac0;font-weight:700}.theme-light .chaosbad{color:#9047c0;font-weight:700}.theme-light .chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.theme-light .sinister{color:purple;font-weight:700;font-style:italic}.theme-light .blob{color:#006221;font-weight:700;font-style:italic}.theme-light .confirm{color:#00af3b}.theme-light .rose{color:#ff5050}.theme-light .sans{font-family:Comic Sans MS,cursive,sans-serif}.theme-light .wingdings{font-family:Wingdings,Webdings}.theme-light .robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.theme-light .ancient{color:#008b8b;font-style:italic}.theme-light .newscaster{color:maroon}.theme-light .mod{color:#735638;font-weight:700}.theme-light .modooc{color:#184880;font-weight:700}.theme-light .adminmod{color:#402a14;font-weight:700}.theme-light .tajaran{color:#803b56}.theme-light .skrell{color:#00ced1}.theme-light .solcom{color:#22228b}.theme-light .com_srus{color:#7c4848}.theme-light .soghun{color:#228b22}.theme-light .changeling{color:purple}.theme-light .vox{color:#a0a}.theme-light .diona{color:#804000;font-weight:700}.theme-light .trinary{color:#727272}.theme-light .kidan{color:#664205}.theme-light .slime{color:#07a}.theme-light .drask{color:#a3d4eb;font-family:Arial Black}.theme-light .moth{color:#869b29;font-family:Copperplate}.theme-light .clown{color:red}.theme-light .vulpkanin{color:#b97a57}.theme-light .abductor{color:purple;font-style:italic}.theme-light .mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.theme-light .rough{font-family:Trebuchet MS,cursive,sans-serif}.theme-light .say_quote{font-family:Georgia,Verdana,sans-serif}.theme-light .cult{color:purple;font-weight:700;font-style:italic}.theme-light .cultspeech{color:#7f0000;font-style:italic}.theme-light .cultitalic{color:#960000;font-style:italic}.theme-light .cultlarge{color:#960000;font-weight:700;font-size:120%}.theme-light .narsie{color:#960000;font-weight:700;font-size:300%}.theme-light .narsiesmall{color:#960000;font-weight:700;font-size:200%}.theme-light .zombie{color:#7c4848}.theme-light .zombielarge{color:#7c4848;font-weight:700;font-size:120%}.theme-light .interface{color:#303}.theme-light .big{font-size:150%}.theme-light .reallybig{font-size:175%}.theme-light .greentext{color:#0f0;font-size:150%}.theme-light .redtext{color:red;font-size:150%}.theme-light .bold{font-weight:700}.theme-light .his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.theme-light .center{text-align:center}.theme-light .red{color:red}.theme-light .purple{color:#5e2d79}.theme-light .skeleton{color:#585858;font-weight:700;font-style:italic}.theme-light .gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.theme-light .orange{color:orange}.theme-light .orangei{color:orange;font-style:italic}.theme-light .orangeb{color:orange;font-weight:700}.theme-light .resonate{color:#298f85}.theme-light .healthscan_oxy{color:#0074bd}.theme-light .revennotice{color:#1d2953}.theme-light .revenboldnotice{color:#1d2953;font-weight:700}.theme-light .revenbignotice{color:#1d2953;font-weight:700;font-size:120%}.theme-light .revenminor{color:#823abb}.theme-light .revenwarning{color:#760fbb;font-style:italic}.theme-light .revendanger{color:#760fbb;font-weight:700;font-size:120%}.theme-light .specialnoticebold{color:#36525e;font-weight:700;font-size:120%}.theme-light .specialnotice{color:#36525e;font-size:120%}.theme-light .medal{font-weight:700}.theme-light .good{color:green}.theme-light .average{color:#ff8000}.theme-light .bad{color:red}.theme-light .italics,.theme-light .talkinto{font-style:italic}.theme-light .whisper{font-style:italic;color:#333}.theme-light .recruit{color:#5c00e6;font-weight:700;font-style:italic}.theme-light .memo{color:#638500;text-align:center}.theme-light .memoedit{text-align:center;font-size:75%}.theme-light .connectionClosed,.theme-light .fatalError{background:red;color:#fff;padding:5px}.theme-light .connectionClosed.restored{background:green}.theme-light .internal.boldnshit{color:#00f;font-weight:700}.theme-light .rebooting{background:#2979af;color:#fff;padding:5px}.theme-light .rebooting a{color:#fff!important;text-decoration-color:#fff!important}.theme-light .text-normal{font-weight:400;font-style:normal}.theme-light .hidden{display:none;visibility:hidden}.theme-light .colossus{color:#7f282a;font-size:175%}.theme-light .hierophant{color:#609;font-weight:700;font-style:italic}.theme-light .hierophant_warning{color:#609;font-style:italic}.theme-light .emoji{max-height:16px;max-width:16px}.theme-light .adminticket{color:#3e7336;font-weight:700}.theme-light .adminticketalt{color:#014c8a;font-weight:700}.theme-light span.body .codephrases{color:#00f}.theme-light span.body .coderesponses{color:red}.theme-light .announcement h1,.theme-light .announcement h2{color:#000;margin:8pt 0;line-height:1.2}.theme-light .announcement p{color:#d82020;line-height:1.3}.theme-light .announcement.minor h1{font-size:180%}.theme-light .announcement.minor h2{font-size:170%}.theme-light .announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.theme-light .bolditalics{font-style:italic;font-weight:700}.theme-light .boxed_message{background:#f7fcff;border:1px solid #111a26;margin:.5em;padding:.5em .75em;text-align:center}.theme-light .boxed_message.left_align_text{text-align:left}.theme-light .boxed_message.red_border{background:#fff7f7;border-color:#a00}.theme-light .boxed_message.green_border{background:#f7fff7;border-color:#0f0}.theme-light .boxed_message.purple_border{background:#fdf7ff;border-color:#a0f}.theme-light .boxed_message.notice_border{background:#f7fdff;border-color:#0000bf}.theme-light .boxed_message.thick_border{border-width:thick}.theme-light .oxygen{color:#006adb}.theme-light .nitrogen{color:#d00a06}.theme-light .carbon_dioxide{color:#1f1f1f}.theme-light .plasma{color:#853c00}.theme-light .sleeping_agent{color:#e82f2c}.theme-light .agent_b{color:#004d4d}.theme-ntos .color-black{color:#1a1a1a!important}.theme-ntos .color-white{color:#fff!important}.theme-ntos .color-red{color:#df3e3e!important}.theme-ntos .color-orange{color:#f37f33!important}.theme-ntos .color-yellow{color:#fbda21!important}.theme-ntos .color-olive{color:#cbe41c!important}.theme-ntos .color-green{color:#25ca4c!important}.theme-ntos .color-teal{color:#00d6cc!important}.theme-ntos .color-blue{color:#2e93de!important}.theme-ntos .color-violet{color:#7349cf!important}.theme-ntos .color-purple{color:#ad45d0!important}.theme-ntos .color-pink{color:#e34da1!important}.theme-ntos .color-brown{color:#b97447!important}.theme-ntos .color-grey{color:#848484!important}.theme-ntos .color-good{color:#68c22d!important}.theme-ntos .color-average{color:#f29a29!important}.theme-ntos .color-bad{color:#df3e3e!important}.theme-ntos .color-label{color:#8b9bb0!important}.theme-ntos .color-gold{color:#f3b22f!important}.theme-ntos .color-bg-black{background-color:#000!important}.theme-ntos .color-bg-white{background-color:#d9d9d9!important}.theme-ntos .color-bg-red{background-color:#bd2020!important}.theme-ntos .color-bg-orange{background-color:#d95e0c!important}.theme-ntos .color-bg-yellow{background-color:#d9b804!important}.theme-ntos .color-bg-olive{background-color:#9aad14!important}.theme-ntos .color-bg-green{background-color:#1b9638!important}.theme-ntos .color-bg-teal{background-color:#009a93!important}.theme-ntos .color-bg-blue{background-color:#1c71b1!important}.theme-ntos .color-bg-violet{background-color:#552dab!important}.theme-ntos .color-bg-purple{background-color:#8b2baa!important}.theme-ntos .color-bg-pink{background-color:#cf2082!important}.theme-ntos .color-bg-brown{background-color:#8c5836!important}.theme-ntos .color-bg-grey{background-color:#646464!important}.theme-ntos .color-bg-good{background-color:#4d9121!important}.theme-ntos .color-bg-average{background-color:#cd7a0d!important}.theme-ntos .color-bg-bad{background-color:#bd2020!important}.theme-ntos .color-bg-label{background-color:#657a94!important}.theme-ntos .color-bg-gold{background-color:#d6920c!important}.theme-ntos .color-border-black{border-color:#1a1a1a!important}.theme-ntos .color-border-white{border-color:#fff!important}.theme-ntos .color-border-red{border-color:#df3e3e!important}.theme-ntos .color-border-orange{border-color:#f37f33!important}.theme-ntos .color-border-yellow{border-color:#fbda21!important}.theme-ntos .color-border-olive{border-color:#cbe41c!important}.theme-ntos .color-border-green{border-color:#25ca4c!important}.theme-ntos .color-border-teal{border-color:#00d6cc!important}.theme-ntos .color-border-blue{border-color:#2e93de!important}.theme-ntos .color-border-violet{border-color:#7349cf!important}.theme-ntos .color-border-purple{border-color:#ad45d0!important}.theme-ntos .color-border-pink{border-color:#e34da1!important}.theme-ntos .color-border-brown{border-color:#b97447!important}.theme-ntos .color-border-grey{border-color:#848484!important}.theme-ntos .color-border-good{border-color:#68c22d!important}.theme-ntos .color-border-average{border-color:#f29a29!important}.theme-ntos .color-border-bad{border-color:#df3e3e!important}.theme-ntos .color-border-label{border-color:#8b9bb0!important}.theme-ntos .color-border-gold{border-color:#f3b22f!important}.theme-ntos .Section{position:relative;margin-bottom:.5em;background-color:#121922;box-sizing:border-box}.theme-ntos .Section:last-child{margin-bottom:0}.theme-ntos .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.theme-ntos .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-ntos .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-ntos .Section__rest{position:relative}.theme-ntos .Section__content{padding:.66em .5em}.theme-ntos .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-ntos .Section--fill{display:flex;flex-direction:column;height:100%}.theme-ntos .Section--fill>.Section__rest{flex-grow:1}.theme-ntos .Section--fill>.Section__rest>.Section__content{height:100%}.theme-ntos .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-ntos .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-ntos .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-ntos .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-ntos .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-ntos .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-ntos .Section .Section:first-child{margin-top:-.5em}.theme-ntos .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-ntos .Section .Section .Section .Section__titleText{font-size:1em}.theme-ntos .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-ntos .Button:last-child{margin-right:0;margin-bottom:0}.theme-ntos .Button .fa,.theme-ntos .Button .fas,.theme-ntos .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-ntos .Button--hasContent .fa,.theme-ntos .Button--hasContent .fas,.theme-ntos .Button--hasContent .far{margin-right:.25em}.theme-ntos .Button--hasContent.Button--iconRight .fa,.theme-ntos .Button--hasContent.Button--iconRight .fas,.theme-ntos .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-ntos .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-ntos .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-ntos .Button--circular{border-radius:50%}.theme-ntos .Button--compact{padding:0 .25em;line-height:1.333em}.theme-ntos .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-ntos .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-ntos .Button--color--black{background-color:#000;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--black:hover{background-color:#101010;color:#fff}.theme-ntos .Button--color--black--translucent{background-color:rgba(0,0,0,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--black--translucent:hover{background-color:rgba(16,16,16,.5);color:#fff}.theme-ntos .Button--color--white{background-color:#d9d9d9;color:#000;transition:color .2s,background-color .2s}.theme-ntos .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-ntos .Button--color--white--translucent{background-color:rgba(217,217,217,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--white--translucent:hover{background-color:rgba(248,248,248,.5);color:#fff}.theme-ntos .Button--color--red{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--red--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--red--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.theme-ntos .Button--color--orange{background-color:#d95e0c;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-ntos .Button--color--orange--translucent{background-color:rgba(217,94,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--orange--translucent:hover{background-color:rgba(239,126,51,.5);color:#fff}.theme-ntos .Button--color--yellow{background-color:#d9b804;color:#000;transition:color .2s,background-color .2s}.theme-ntos .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--yellow--translucent{background-color:rgba(217,184,4,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--yellow--translucent:hover{background-color:rgba(245,213,35,.5);color:#fff}.theme-ntos .Button--color--olive{background-color:#9aad14;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-ntos .Button--color--olive--translucent{background-color:rgba(154,173,20,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--olive--translucent:hover{background-color:rgba(189,211,39,.5);color:#fff}.theme-ntos .Button--color--green{background-color:#1b9638;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-ntos .Button--color--green--translucent{background-color:rgba(27,150,56,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--green--translucent:hover{background-color:rgba(47,185,79,.5);color:#fff}.theme-ntos .Button--color--teal{background-color:#009a93;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-ntos .Button--color--teal--translucent{background-color:rgba(0,154,147,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--teal--translucent:hover{background-color:rgba(16,189,182,.5);color:#fff}.theme-ntos .Button--color--blue{background-color:#1c71b1;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-ntos .Button--color--blue--translucent{background-color:rgba(28,113,177,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--blue--translucent:hover{background-color:rgba(48,143,214,.5);color:#fff}.theme-ntos .Button--color--violet{background-color:#552dab;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-ntos .Button--color--violet--translucent{background-color:rgba(85,45,171,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--violet--translucent:hover{background-color:rgba(114,73,202,.5);color:#fff}.theme-ntos .Button--color--purple{background-color:#8b2baa;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-ntos .Button--color--purple--translucent{background-color:rgba(139,43,170,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--purple--translucent:hover{background-color:rgba(170,70,202,.5);color:#fff}.theme-ntos .Button--color--pink{background-color:#cf2082;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-ntos .Button--color--pink--translucent{background-color:rgba(207,32,130,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--pink--translucent:hover{background-color:rgba(224,76,160,.5);color:#fff}.theme-ntos .Button--color--brown{background-color:#8c5836;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-ntos .Button--color--brown--translucent{background-color:rgba(140,88,54,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--brown--translucent:hover{background-color:rgba(174,114,76,.5);color:#fff}.theme-ntos .Button--color--grey{background-color:#646464;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-ntos .Button--color--grey--translucent{background-color:rgba(100,100,100,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--grey--translucent:hover{background-color:rgba(129,129,129,.5);color:#fff}.theme-ntos .Button--color--good{background-color:#4d9121;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-ntos .Button--color--good--translucent{background-color:rgba(77,145,33,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--good--translucent:hover{background-color:rgba(103,179,53,.5);color:#fff}.theme-ntos .Button--color--average{background-color:#cd7a0d;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-ntos .Button--color--average--translucent{background-color:rgba(205,122,13,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--average--translucent:hover{background-color:rgba(235,151,43,.5);color:#fff}.theme-ntos .Button--color--bad{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--bad--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--bad--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.theme-ntos .Button--color--label{background-color:#657a94;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--label:hover{background-color:#8a9aae;color:#fff}.theme-ntos .Button--color--label--translucent{background-color:rgba(101,122,148,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--label--translucent:hover{background-color:rgba(138,154,174,.5);color:#fff}.theme-ntos .Button--color--gold{background-color:#d6920c;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-ntos .Button--color--gold--translucent{background-color:rgba(214,146,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--gold--translucent:hover{background-color:rgba(238,175,48,.5);color:#fff}.theme-ntos .Button--color--transparent{background-color:rgba(27,38,51,0);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--transparent:hover{background-color:rgba(44,57,73,.81);color:#fff}.theme-ntos .Button--color--default{background-color:#384e68;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--default:hover{background-color:#4f6885;color:#fff}.theme-ntos .Button--color--default--translucent{background-color:rgba(35,47,60,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--default--translucent:hover{background-color:rgba(56,69,84,.5);color:#fff}.theme-ntos .Button--color--caution{background-color:#d9b804;color:#000;transition:color .2s,background-color .2s}.theme-ntos .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--caution--translucent{background-color:rgba(217,184,4,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--caution--translucent:hover{background-color:rgba(245,213,35,.5);color:#fff}.theme-ntos .Button--color--danger{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--color--danger:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--danger--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-ntos .Button--color--danger--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.theme-ntos .Button--disabled{background-color:#999!important;color:rgba(255,255,255,.75)!important}.theme-ntos .Button--disabled--translucent{background-color:rgba(77,23,23,.5)!important;color:rgba(255,255,255,.5)!important}.theme-ntos .Button--selected,.theme-ntos .Button--selected--translucent{background-color:#1b9638;color:#fff;transition:color .2s,background-color .2s}.theme-ntos .Button--selected:hover,.theme-ntos .Button--selected--translucent:hover{background-color:#2fb94f;color:#fff}.theme-ntos .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-ntos .NumberInput--fluid{display:block}.theme-ntos .NumberInput__content{margin-left:.5em}.theme-ntos .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-ntos .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.theme-ntos .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-ntos .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-ntos .Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-ntos .Input--fluid{display:block;width:auto}.theme-ntos .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-ntos .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-ntos .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-ntos .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-ntos .TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-ntos .TextArea--fluid{display:block;width:auto;height:auto}.theme-ntos .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-ntos .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-ntos .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-ntos .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-ntos .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-ntos .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-ntos .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-ntos .Knob__popupValue,.theme-ntos .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .Knob__popupValue--right{top:.25rem;right:-50%}.theme-ntos .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-ntos .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-ntos .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-ntos .Knob__ringFillPivot{transform:rotate(135deg)}.theme-ntos .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-ntos .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-ntos .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-ntos .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-ntos .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-ntos .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-ntos .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-ntos .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-ntos .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-ntos .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-ntos .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-ntos .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-ntos .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-ntos .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-ntos .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-ntos .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-ntos .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-ntos .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.theme-ntos .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-ntos .Slider:not(.Slider__disabled){cursor:e-resize}.theme-ntos .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-ntos .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-ntos .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-ntos .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-ntos .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-ntos .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-ntos .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-ntos .ProgressBar--color--default{border:.0833333333em solid #3e6189}.theme-ntos .ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.theme-ntos .ProgressBar--color--disabled{border:1px solid #999}.theme-ntos .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-ntos .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-ntos .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-ntos .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-ntos .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-ntos .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-ntos .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-ntos .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-ntos .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-ntos .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-ntos .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-ntos .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-ntos .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-ntos .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-ntos .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-ntos .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-ntos .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-ntos .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-ntos .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-ntos .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-ntos .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-ntos .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-ntos .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-ntos .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-ntos .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-ntos .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-ntos .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-ntos .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-ntos .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-ntos .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-ntos .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-ntos .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--label{border:.0833333333em solid #657a94!important}.theme-ntos .ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.theme-ntos .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-ntos .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-ntos .Chat{color:#abc6ec}.theme-ntos .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-ntos .Chat__badge:before{content:"x"}.theme-ntos .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-ntos .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-ntos .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-ntos .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#121922}.theme-ntos .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-ntos .Chat__highlight{color:#000}.theme-ntos .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-ntos .ChatMessage{word-wrap:break-word}.theme-ntos .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-ntos .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-ntos html,.theme-ntos body{scrollbar-color:#2a3b4f #141d26}.theme-ntos .Layout,.theme-ntos .Layout *{scrollbar-base-color:#141d26;scrollbar-face-color:#2a3b4f;scrollbar-3dlight-color:#1b2633;scrollbar-highlight-color:#1b2633;scrollbar-track-color:#141d26;scrollbar-arrow-color:#7290b4;scrollbar-shadow-color:#2a3b4f}.theme-ntos .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-ntos .Layout__content--flexRow{display:flex;flex-flow:row}.theme-ntos .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-ntos .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-ntos .Layout__content--noMargin{margin:0}.theme-ntos .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#1b2633;background-image:linear-gradient(to bottom,#1b2633,#1b2633)}.theme-ntos .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-ntos .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-ntos .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-ntos .Window__contentPadding:after{height:0}.theme-ntos .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-ntos .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(50,63,78,.25);pointer-events:none}.theme-ntos .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-ntos .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-ntos .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-ntos .TitleBar{background-color:#1b2633;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-ntos .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#1b2633;transition:color .25s,background-color .25s}.theme-ntos .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-ntos .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-ntos .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-ntos .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-ntos .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-ntos .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-ntos .boxed_message{background:#1c242e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-ntos .boxed_message.left_align_text{text-align:left}.theme-ntos .boxed_message.red_border{background:#2e1c1c;border-color:#a00}.theme-ntos .boxed_message.green_border{background:#1c2e22;border-color:#0f0}.theme-ntos .boxed_message.purple_border{background:#221c2e;border-color:#8000ff}.theme-ntos .boxed_message.notice_border{background:#1f2633;border-color:#6685f5}.theme-ntos .boxed_message.thick_border{border-width:thick}.theme-syndicate .color-black{color:#1a1a1a!important}.theme-syndicate .color-white{color:#fff!important}.theme-syndicate .color-red{color:#df3e3e!important}.theme-syndicate .color-orange{color:#f37f33!important}.theme-syndicate .color-yellow{color:#fbda21!important}.theme-syndicate .color-olive{color:#cbe41c!important}.theme-syndicate .color-green{color:#25ca4c!important}.theme-syndicate .color-teal{color:#00d6cc!important}.theme-syndicate .color-blue{color:#2e93de!important}.theme-syndicate .color-violet{color:#7349cf!important}.theme-syndicate .color-purple{color:#ad45d0!important}.theme-syndicate .color-pink{color:#e34da1!important}.theme-syndicate .color-brown{color:#b97447!important}.theme-syndicate .color-grey{color:#848484!important}.theme-syndicate .color-good{color:#68c22d!important}.theme-syndicate .color-average{color:#f29a29!important}.theme-syndicate .color-bad{color:#df3e3e!important}.theme-syndicate .color-label{color:#8b9bb0!important}.theme-syndicate .color-gold{color:#f3b22f!important}.theme-syndicate .color-bg-black{background-color:#000!important}.theme-syndicate .color-bg-white{background-color:#d9d9d9!important}.theme-syndicate .color-bg-red{background-color:#bd2020!important}.theme-syndicate .color-bg-orange{background-color:#d95e0c!important}.theme-syndicate .color-bg-yellow{background-color:#d9b804!important}.theme-syndicate .color-bg-olive{background-color:#9aad14!important}.theme-syndicate .color-bg-green{background-color:#1b9638!important}.theme-syndicate .color-bg-teal{background-color:#009a93!important}.theme-syndicate .color-bg-blue{background-color:#1c71b1!important}.theme-syndicate .color-bg-violet{background-color:#552dab!important}.theme-syndicate .color-bg-purple{background-color:#8b2baa!important}.theme-syndicate .color-bg-pink{background-color:#cf2082!important}.theme-syndicate .color-bg-brown{background-color:#8c5836!important}.theme-syndicate .color-bg-grey{background-color:#646464!important}.theme-syndicate .color-bg-good{background-color:#4d9121!important}.theme-syndicate .color-bg-average{background-color:#cd7a0d!important}.theme-syndicate .color-bg-bad{background-color:#bd2020!important}.theme-syndicate .color-bg-label{background-color:#657a94!important}.theme-syndicate .color-bg-gold{background-color:#d6920c!important}.theme-syndicate .color-border-black{border-color:#1a1a1a!important}.theme-syndicate .color-border-white{border-color:#fff!important}.theme-syndicate .color-border-red{border-color:#df3e3e!important}.theme-syndicate .color-border-orange{border-color:#f37f33!important}.theme-syndicate .color-border-yellow{border-color:#fbda21!important}.theme-syndicate .color-border-olive{border-color:#cbe41c!important}.theme-syndicate .color-border-green{border-color:#25ca4c!important}.theme-syndicate .color-border-teal{border-color:#00d6cc!important}.theme-syndicate .color-border-blue{border-color:#2e93de!important}.theme-syndicate .color-border-violet{border-color:#7349cf!important}.theme-syndicate .color-border-purple{border-color:#ad45d0!important}.theme-syndicate .color-border-pink{border-color:#e34da1!important}.theme-syndicate .color-border-brown{border-color:#b97447!important}.theme-syndicate .color-border-grey{border-color:#848484!important}.theme-syndicate .color-border-good{border-color:#68c22d!important}.theme-syndicate .color-border-average{border-color:#f29a29!important}.theme-syndicate .color-border-bad{border-color:#df3e3e!important}.theme-syndicate .color-border-label{border-color:#8b9bb0!important}.theme-syndicate .color-border-gold{border-color:#f3b22f!important}.theme-syndicate .Section{position:relative;margin-bottom:.5em;background-color:#2b0101;box-sizing:border-box}.theme-syndicate .Section:last-child{margin-bottom:0}.theme-syndicate .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #397439}.theme-syndicate .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-syndicate .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-syndicate .Section__rest{position:relative}.theme-syndicate .Section__content{padding:.66em .5em}.theme-syndicate .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-syndicate .Section--fill{display:flex;flex-direction:column;height:100%}.theme-syndicate .Section--fill>.Section__rest{flex-grow:1}.theme-syndicate .Section--fill>.Section__rest>.Section__content{height:100%}.theme-syndicate .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-syndicate .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-syndicate .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-syndicate .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-syndicate .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-syndicate .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-syndicate .Section .Section:first-child{margin-top:-.5em}.theme-syndicate .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-syndicate .Section .Section .Section .Section__titleText{font-size:1em}.theme-syndicate .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-syndicate .Button:last-child{margin-right:0;margin-bottom:0}.theme-syndicate .Button .fa,.theme-syndicate .Button .fas,.theme-syndicate .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-syndicate .Button--hasContent .fa,.theme-syndicate .Button--hasContent .fas,.theme-syndicate .Button--hasContent .far{margin-right:.25em}.theme-syndicate .Button--hasContent.Button--iconRight .fa,.theme-syndicate .Button--hasContent.Button--iconRight .fas,.theme-syndicate .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-syndicate .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-syndicate .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-syndicate .Button--circular{border-radius:50%}.theme-syndicate .Button--compact{padding:0 .25em;line-height:1.333em}.theme-syndicate .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-syndicate .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-syndicate .Button--color--black{background-color:#000;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--black:hover{background-color:#101010;color:#fff}.theme-syndicate .Button--color--black--translucent{background-color:rgba(0,0,0,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--black--translucent:hover{background-color:rgba(16,16,16,.5);color:#fff}.theme-syndicate .Button--color--white{background-color:#d9d9d9;color:#000;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-syndicate .Button--color--white--translucent{background-color:rgba(217,217,217,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--white--translucent:hover{background-color:rgba(248,248,248,.5);color:#fff}.theme-syndicate .Button--color--red{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--red--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--red--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.theme-syndicate .Button--color--orange{background-color:#d95e0c;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-syndicate .Button--color--orange--translucent{background-color:rgba(217,94,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--orange--translucent:hover{background-color:rgba(239,126,51,.5);color:#fff}.theme-syndicate .Button--color--yellow{background-color:#d9b804;color:#000;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-syndicate .Button--color--yellow--translucent{background-color:rgba(217,184,4,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--yellow--translucent:hover{background-color:rgba(245,213,35,.5);color:#fff}.theme-syndicate .Button--color--olive{background-color:#9aad14;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-syndicate .Button--color--olive--translucent{background-color:rgba(154,173,20,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--olive--translucent:hover{background-color:rgba(189,211,39,.5);color:#fff}.theme-syndicate .Button--color--green{background-color:#1b9638;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-syndicate .Button--color--green--translucent{background-color:rgba(27,150,56,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--green--translucent:hover{background-color:rgba(47,185,79,.5);color:#fff}.theme-syndicate .Button--color--teal{background-color:#009a93;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-syndicate .Button--color--teal--translucent{background-color:rgba(0,154,147,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--teal--translucent:hover{background-color:rgba(16,189,182,.5);color:#fff}.theme-syndicate .Button--color--blue{background-color:#1c71b1;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-syndicate .Button--color--blue--translucent{background-color:rgba(28,113,177,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--blue--translucent:hover{background-color:rgba(48,143,214,.5);color:#fff}.theme-syndicate .Button--color--violet{background-color:#552dab;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-syndicate .Button--color--violet--translucent{background-color:rgba(85,45,171,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--violet--translucent:hover{background-color:rgba(114,73,202,.5);color:#fff}.theme-syndicate .Button--color--purple{background-color:#8b2baa;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-syndicate .Button--color--purple--translucent{background-color:rgba(139,43,170,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--purple--translucent:hover{background-color:rgba(170,70,202,.5);color:#fff}.theme-syndicate .Button--color--pink{background-color:#cf2082;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-syndicate .Button--color--pink--translucent{background-color:rgba(207,32,130,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--pink--translucent:hover{background-color:rgba(224,76,160,.5);color:#fff}.theme-syndicate .Button--color--brown{background-color:#8c5836;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-syndicate .Button--color--brown--translucent{background-color:rgba(140,88,54,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--brown--translucent:hover{background-color:rgba(174,114,76,.5);color:#fff}.theme-syndicate .Button--color--grey{background-color:#646464;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-syndicate .Button--color--grey--translucent{background-color:rgba(100,100,100,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--grey--translucent:hover{background-color:rgba(129,129,129,.5);color:#fff}.theme-syndicate .Button--color--good{background-color:#4d9121;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-syndicate .Button--color--good--translucent{background-color:rgba(77,145,33,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--good--translucent:hover{background-color:rgba(103,179,53,.5);color:#fff}.theme-syndicate .Button--color--average{background-color:#cd7a0d;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-syndicate .Button--color--average--translucent{background-color:rgba(205,122,13,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--average--translucent:hover{background-color:rgba(235,151,43,.5);color:#fff}.theme-syndicate .Button--color--bad{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--bad--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--bad--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.theme-syndicate .Button--color--label{background-color:#657a94;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--label:hover{background-color:#8a9aae;color:#fff}.theme-syndicate .Button--color--label--translucent{background-color:rgba(101,122,148,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--label--translucent:hover{background-color:rgba(138,154,174,.5);color:#fff}.theme-syndicate .Button--color--gold{background-color:#d6920c;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-syndicate .Button--color--gold--translucent{background-color:rgba(214,146,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--gold--translucent:hover{background-color:rgba(238,175,48,.5);color:#fff}.theme-syndicate .Button--color--transparent{background-color:rgba(77,2,2,0);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--transparent:hover{background-color:rgba(103,14,14,.81);color:#fff}.theme-syndicate .Button--color--default{background-color:#397439;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--default:hover{background-color:#509350;color:#fff}.theme-syndicate .Button--color--default--translucent{background-color:rgba(88,8,8,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--default--translucent:hover{background-color:rgba(115,25,25,.5);color:#fff}.theme-syndicate .Button--color--caution{background-color:#be6209;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-syndicate .Button--color--caution--translucent{background-color:rgba(190,98,9,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--caution--translucent:hover{background-color:rgba(230,127,26,.5);color:#fff}.theme-syndicate .Button--color--danger{background-color:#9a9d00;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-syndicate .Button--color--danger--translucent{background-color:rgba(154,157,0,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-syndicate .Button--color--danger--translucent:hover{background-color:rgba(190,193,16,.5);color:#fff}.theme-syndicate .Button--disabled{background-color:#363636!important;color:rgba(255,255,255,.75)!important}.theme-syndicate .Button--disabled--translucent{background-color:rgba(77,23,23,.5)!important;color:rgba(255,255,255,.5)!important}.theme-syndicate .Button--selected,.theme-syndicate .Button--selected--translucent{background-color:#9d0808;color:#fff;transition:color .2s,background-color .2s}.theme-syndicate .Button--selected:hover,.theme-syndicate .Button--selected--translucent:hover{background-color:#c11919;color:#fff}.theme-syndicate .NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#fff;background-color:#910101;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.theme-syndicate .NoticeBox--color--black{color:#fff;background-color:#000}.theme-syndicate .NoticeBox--color--white{color:#000;background-color:#b3b3b3}.theme-syndicate .NoticeBox--color--red{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--orange{color:#fff;background-color:#854114}.theme-syndicate .NoticeBox--color--yellow{color:#000;background-color:#83710d}.theme-syndicate .NoticeBox--color--olive{color:#000;background-color:#576015}.theme-syndicate .NoticeBox--color--green{color:#fff;background-color:#174e24}.theme-syndicate .NoticeBox--color--teal{color:#fff;background-color:#064845}.theme-syndicate .NoticeBox--color--blue{color:#fff;background-color:#1b4565}.theme-syndicate .NoticeBox--color--violet{color:#fff;background-color:#3b2864}.theme-syndicate .NoticeBox--color--purple{color:#fff;background-color:#542663}.theme-syndicate .NoticeBox--color--pink{color:#fff;background-color:#802257}.theme-syndicate .NoticeBox--color--brown{color:#fff;background-color:#4c3729}.theme-syndicate .NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.theme-syndicate .NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.theme-syndicate .NoticeBox--color--average{color:#fff;background-color:#7b4e13}.theme-syndicate .NoticeBox--color--bad{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--label{color:#fff;background-color:#53565a}.theme-syndicate .NoticeBox--color--gold{color:#fff;background-color:#825d13}.theme-syndicate .NoticeBox--type--info{color:#fff;background-color:#235982}.theme-syndicate .NoticeBox--type--success{color:#fff;background-color:#1e662f}.theme-syndicate .NoticeBox--type--warning{color:#fff;background-color:#a95219}.theme-syndicate .NoticeBox--type--danger{color:#fff;background-color:#8f2828}.theme-syndicate .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;color:#87ce87;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-syndicate .NumberInput--fluid{display:block}.theme-syndicate .NumberInput__content{margin-left:.5em}.theme-syndicate .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-syndicate .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #87ce87;background-color:#87ce87}.theme-syndicate .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-syndicate .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-syndicate .Input--disabled{color:#777;border-color:#6b6b6b;border-color:rgba(107,107,107,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-syndicate .Input--fluid{display:block;width:auto}.theme-syndicate .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-syndicate .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-syndicate .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-syndicate .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-syndicate .TextArea{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-syndicate .TextArea--fluid{display:block;width:auto;height:auto}.theme-syndicate .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-syndicate .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-syndicate .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-syndicate .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-syndicate .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-syndicate .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-syndicate .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-syndicate .Knob__popupValue,.theme-syndicate .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .Knob__popupValue--right{top:.25rem;right:-50%}.theme-syndicate .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-syndicate .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-syndicate .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-syndicate .Knob__ringFillPivot{transform:rotate(135deg)}.theme-syndicate .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-syndicate .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-syndicate .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-syndicate .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-syndicate .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-syndicate .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-syndicate .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-syndicate .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-syndicate .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-syndicate .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-syndicate .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-syndicate .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-syndicate .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-syndicate .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-syndicate .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-syndicate .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-syndicate .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-syndicate .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.theme-syndicate .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-syndicate .Slider:not(.Slider__disabled){cursor:e-resize}.theme-syndicate .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-syndicate .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-syndicate .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-syndicate .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,.5);transition:border-color .5s}.theme-syndicate .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-syndicate .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-syndicate .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-syndicate .ProgressBar--color--default{border:.0833333333em solid #306330}.theme-syndicate .ProgressBar--color--default .ProgressBar__fill{background-color:#306330}.theme-syndicate .ProgressBar--color--disabled{border:1px solid #999}.theme-syndicate .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-syndicate .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-syndicate .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-syndicate .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-syndicate .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-syndicate .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-syndicate .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-syndicate .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-syndicate .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-syndicate .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-syndicate .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-syndicate .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-syndicate .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-syndicate .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-syndicate .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-syndicate .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-syndicate .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-syndicate .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-syndicate .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-syndicate .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-syndicate .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-syndicate .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-syndicate .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-syndicate .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-syndicate .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-syndicate .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-syndicate .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-syndicate .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-syndicate .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-syndicate .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-syndicate .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-syndicate .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--label{border:.0833333333em solid #657a94!important}.theme-syndicate .ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.theme-syndicate .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-syndicate .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-syndicate .Chat{color:#abc6ec}.theme-syndicate .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-syndicate .Chat__badge:before{content:"x"}.theme-syndicate .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-syndicate .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-syndicate .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-syndicate .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#2b0101}.theme-syndicate .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-syndicate .Chat__highlight{color:#000}.theme-syndicate .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-syndicate .ChatMessage{word-wrap:break-word}.theme-syndicate .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-syndicate .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-syndicate html,.theme-syndicate body{scrollbar-color:#770303 #3a0202}.theme-syndicate .Layout,.theme-syndicate .Layout *{scrollbar-base-color:#3a0202;scrollbar-face-color:#770303;scrollbar-3dlight-color:#4d0202;scrollbar-highlight-color:#4d0202;scrollbar-track-color:#3a0202;scrollbar-arrow-color:#fa2d2d;scrollbar-shadow-color:#770303}.theme-syndicate .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-syndicate .Layout__content--flexRow{display:flex;flex-flow:row}.theme-syndicate .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-syndicate .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-syndicate .Layout__content--noMargin{margin:0}.theme-syndicate .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#4d0202;background-image:linear-gradient(to bottom,#4d0202,#4d0202)}.theme-syndicate .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-syndicate .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-syndicate .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-syndicate .Window__contentPadding:after{height:0}.theme-syndicate .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-syndicate .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(108,22,22,.25);pointer-events:none}.theme-syndicate .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-syndicate .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-syndicate .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-syndicate .TitleBar{background-color:#910101;border-bottom:1px solid #161616;box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-syndicate .TitleBar__clickable{color:rgba(255,255,255,.5);background-color:#910101;transition:color .25s,background-color .25s}.theme-syndicate .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-syndicate .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,255,255,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-syndicate .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-syndicate .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-syndicate .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-syndicate .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-syndicate .adminooc{color:#29ccbe}.theme-syndicate .debug{color:#8f39e6}.theme-syndicate .boxed_message{background:rgba(20,20,35,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-syndicate .boxed_message.left_align_text{text-align:left}.theme-syndicate .boxed_message.red_border{background:rgba(0,0,0,.2);border-color:red}.theme-syndicate .boxed_message.green_border{background:rgba(0,75,0,.25);border-color:#0f0}.theme-syndicate .boxed_message.purple_border{background:rgba(25,0,50,.25);border-color:#8000ff}.theme-syndicate .boxed_message.notice_border{background:rgba(0,0,75,.25);border-color:#6685f5}.theme-syndicate .boxed_message.thick_border{border-width:thick}.theme-paradise .color-black{color:#1a1a1a!important}.theme-paradise .color-white{color:#fff!important}.theme-paradise .color-red{color:#df3e3e!important}.theme-paradise .color-orange{color:#f37f33!important}.theme-paradise .color-yellow{color:#fbda21!important}.theme-paradise .color-olive{color:#cbe41c!important}.theme-paradise .color-green{color:#25ca4c!important}.theme-paradise .color-teal{color:#00d6cc!important}.theme-paradise .color-blue{color:#2e93de!important}.theme-paradise .color-violet{color:#7349cf!important}.theme-paradise .color-purple{color:#ad45d0!important}.theme-paradise .color-pink{color:#e34da1!important}.theme-paradise .color-brown{color:#b97447!important}.theme-paradise .color-grey{color:#848484!important}.theme-paradise .color-good{color:#68c22d!important}.theme-paradise .color-average{color:#f29a29!important}.theme-paradise .color-bad{color:#df3e3e!important}.theme-paradise .color-label{color:#955d4b!important}.theme-paradise .color-gold{color:#f3b22f!important}.theme-paradise .color-bg-black{background-color:#000!important}.theme-paradise .color-bg-white{background-color:#d9d9d9!important}.theme-paradise .color-bg-red{background-color:#bd2020!important}.theme-paradise .color-bg-orange{background-color:#d95e0c!important}.theme-paradise .color-bg-yellow{background-color:#d9b804!important}.theme-paradise .color-bg-olive{background-color:#9aad14!important}.theme-paradise .color-bg-green{background-color:#1b9638!important}.theme-paradise .color-bg-teal{background-color:#009a93!important}.theme-paradise .color-bg-blue{background-color:#1c71b1!important}.theme-paradise .color-bg-violet{background-color:#552dab!important}.theme-paradise .color-bg-purple{background-color:#8b2baa!important}.theme-paradise .color-bg-pink{background-color:#cf2082!important}.theme-paradise .color-bg-brown{background-color:#8c5836!important}.theme-paradise .color-bg-grey{background-color:#646464!important}.theme-paradise .color-bg-good{background-color:#4d9121!important}.theme-paradise .color-bg-average{background-color:#cd7a0d!important}.theme-paradise .color-bg-bad{background-color:#bd2020!important}.theme-paradise .color-bg-label{background-color:#6d4436!important}.theme-paradise .color-bg-gold{background-color:#d6920c!important}.theme-paradise .color-border-black{border-color:#1a1a1a!important}.theme-paradise .color-border-white{border-color:#fff!important}.theme-paradise .color-border-red{border-color:#df3e3e!important}.theme-paradise .color-border-orange{border-color:#f37f33!important}.theme-paradise .color-border-yellow{border-color:#fbda21!important}.theme-paradise .color-border-olive{border-color:#cbe41c!important}.theme-paradise .color-border-green{border-color:#25ca4c!important}.theme-paradise .color-border-teal{border-color:#00d6cc!important}.theme-paradise .color-border-blue{border-color:#2e93de!important}.theme-paradise .color-border-violet{border-color:#7349cf!important}.theme-paradise .color-border-purple{border-color:#ad45d0!important}.theme-paradise .color-border-pink{border-color:#e34da1!important}.theme-paradise .color-border-brown{border-color:#b97447!important}.theme-paradise .color-border-grey{border-color:#848484!important}.theme-paradise .color-border-good{border-color:#68c22d!important}.theme-paradise .color-border-average{border-color:#f29a29!important}.theme-paradise .color-border-bad{border-color:#df3e3e!important}.theme-paradise .color-border-label{border-color:#955d4b!important}.theme-paradise .color-border-gold{border-color:#f3b22f!important}.theme-paradise .Section{position:relative;margin-bottom:.5em;background-color:#40071a;background-color:rgba(0,0,0,.5);box-sizing:border-box}.theme-paradise .Section:last-child{margin-bottom:0}.theme-paradise .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #208080}.theme-paradise .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-paradise .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-paradise .Section__rest{position:relative}.theme-paradise .Section__content{padding:.66em .5em}.theme-paradise .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-paradise .Section--fill{display:flex;flex-direction:column;height:100%}.theme-paradise .Section--fill>.Section__rest{flex-grow:1}.theme-paradise .Section--fill>.Section__rest>.Section__content{height:100%}.theme-paradise .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-paradise .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-paradise .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-paradise .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-paradise .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-paradise .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-paradise .Section .Section:first-child{margin-top:-.5em}.theme-paradise .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-paradise .Section .Section .Section .Section__titleText{font-size:1em}.theme-paradise .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-paradise .Button:last-child{margin-right:0;margin-bottom:0}.theme-paradise .Button .fa,.theme-paradise .Button .fas,.theme-paradise .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-paradise .Button--hasContent .fa,.theme-paradise .Button--hasContent .fas,.theme-paradise .Button--hasContent .far{margin-right:.25em}.theme-paradise .Button--hasContent.Button--iconRight .fa,.theme-paradise .Button--hasContent.Button--iconRight .fas,.theme-paradise .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-paradise .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-paradise .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-paradise .Button--circular{border-radius:50%}.theme-paradise .Button--compact{padding:0 .25em;line-height:1.333em}.theme-paradise .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-paradise .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-paradise .Button--color--black{background-color:#000;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--black:hover{background-color:#101010;color:#fff}.theme-paradise .Button--color--black--translucent{background-color:rgba(0,0,0,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--black--translucent:hover{background-color:rgba(16,16,16,.5);color:#fff}.theme-paradise .Button--color--white{background-color:#d9d9d9;color:#000;transition:color .2s,background-color .2s}.theme-paradise .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-paradise .Button--color--white--translucent{background-color:rgba(217,217,217,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--white--translucent:hover{background-color:rgba(248,248,248,.5);color:#fff}.theme-paradise .Button--color--red{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--red--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--red--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.theme-paradise .Button--color--orange{background-color:#d95e0c;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-paradise .Button--color--orange--translucent{background-color:rgba(217,94,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--orange--translucent:hover{background-color:rgba(239,126,51,.5);color:#fff}.theme-paradise .Button--color--yellow{background-color:#d9b804;color:#000;transition:color .2s,background-color .2s}.theme-paradise .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--yellow--translucent{background-color:rgba(217,184,4,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--yellow--translucent:hover{background-color:rgba(245,213,35,.5);color:#fff}.theme-paradise .Button--color--olive{background-color:#9aad14;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-paradise .Button--color--olive--translucent{background-color:rgba(154,173,20,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--olive--translucent:hover{background-color:rgba(189,211,39,.5);color:#fff}.theme-paradise .Button--color--green{background-color:#1b9638;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-paradise .Button--color--green--translucent{background-color:rgba(27,150,56,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--green--translucent:hover{background-color:rgba(47,185,79,.5);color:#fff}.theme-paradise .Button--color--teal{background-color:#009a93;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-paradise .Button--color--teal--translucent{background-color:rgba(0,154,147,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--teal--translucent:hover{background-color:rgba(16,189,182,.5);color:#fff}.theme-paradise .Button--color--blue{background-color:#1c71b1;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-paradise .Button--color--blue--translucent{background-color:rgba(28,113,177,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--blue--translucent:hover{background-color:rgba(48,143,214,.5);color:#fff}.theme-paradise .Button--color--violet{background-color:#552dab;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-paradise .Button--color--violet--translucent{background-color:rgba(85,45,171,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--violet--translucent:hover{background-color:rgba(114,73,202,.5);color:#fff}.theme-paradise .Button--color--purple{background-color:#8b2baa;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-paradise .Button--color--purple--translucent{background-color:rgba(139,43,170,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--purple--translucent:hover{background-color:rgba(170,70,202,.5);color:#fff}.theme-paradise .Button--color--pink{background-color:#cf2082;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-paradise .Button--color--pink--translucent{background-color:rgba(207,32,130,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--pink--translucent:hover{background-color:rgba(224,76,160,.5);color:#fff}.theme-paradise .Button--color--brown{background-color:#8c5836;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-paradise .Button--color--brown--translucent{background-color:rgba(140,88,54,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--brown--translucent:hover{background-color:rgba(174,114,76,.5);color:#fff}.theme-paradise .Button--color--grey{background-color:#646464;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-paradise .Button--color--grey--translucent{background-color:rgba(100,100,100,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--grey--translucent:hover{background-color:rgba(129,129,129,.5);color:#fff}.theme-paradise .Button--color--good{background-color:#4d9121;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-paradise .Button--color--good--translucent{background-color:rgba(77,145,33,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--good--translucent:hover{background-color:rgba(103,179,53,.5);color:#fff}.theme-paradise .Button--color--average{background-color:#cd7a0d;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-paradise .Button--color--average--translucent{background-color:rgba(205,122,13,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--average--translucent:hover{background-color:rgba(235,151,43,.5);color:#fff}.theme-paradise .Button--color--bad{background-color:#bd2020;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--bad--translucent{background-color:rgba(189,32,32,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--bad--translucent:hover{background-color:rgba(217,63,63,.5);color:#fff}.theme-paradise .Button--color--label{background-color:#6d4436;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--label:hover{background-color:#8b5d4d;color:#fff}.theme-paradise .Button--color--label--translucent{background-color:rgba(109,68,54,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--label--translucent:hover{background-color:rgba(139,93,77,.5);color:#fff}.theme-paradise .Button--color--gold{background-color:#d6920c;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-paradise .Button--color--gold--translucent{background-color:rgba(214,146,12,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--gold--translucent:hover{background-color:rgba(238,175,48,.5);color:#fff}.theme-paradise .Button--color--transparent{background-color:rgba(128,13,51,0);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--transparent:hover{background-color:rgba(164,27,73,.81);color:#fff}.theme-paradise .Button--color--default{background-color:#208080;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--default:hover{background-color:#34a0a0;color:#fff}.theme-paradise .Button--color--default--translucent{background-color:rgba(141,20,60,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--default--translucent:hover{background-color:rgba(175,39,84,.5);color:#fff}.theme-paradise .Button--color--caution{background-color:#d9b804;color:#000;transition:color .2s,background-color .2s}.theme-paradise .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--caution--translucent{background-color:rgba(217,184,4,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--caution--translucent:hover{background-color:rgba(245,213,35,.5);color:#fff}.theme-paradise .Button--color--danger{background-color:#8c1eff;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--color--danger:hover{background-color:#ae61ff;color:#fff}.theme-paradise .Button--color--danger--translucent{background-color:rgba(140,30,255,.33);color:rgba(255,255,255,.5);transition:color .2s,background-color .2s}.theme-paradise .Button--color--danger--translucent:hover{background-color:rgba(174,97,255,.5);color:#fff}.theme-paradise .Button--disabled{background-color:#999!important;color:rgba(255,255,255,.75)!important}.theme-paradise .Button--disabled--translucent{background-color:rgba(77,23,23,.5)!important;color:rgba(255,255,255,.5)!important}.theme-paradise .Button--selected,.theme-paradise .Button--selected--translucent{background-color:#bf6030;color:#fff;transition:color .2s,background-color .2s}.theme-paradise .Button--selected:hover,.theme-paradise .Button--selected--translucent:hover{background-color:#d4835a;color:#fff}.theme-paradise .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;color:#e65c2e;background-color:rgba(0,0,0,.25);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-paradise .NumberInput--fluid{display:block}.theme-paradise .NumberInput__content{margin-left:.5em}.theme-paradise .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-paradise .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #e65c2e;background-color:#e65c2e}.theme-paradise .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,.25);color:#fff;text-align:right}.theme-paradise .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-paradise .Input--disabled{color:#777;border-color:#4a4a4a;border-color:rgba(74,74,74,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-paradise .Input--fluid{display:block;width:auto}.theme-paradise .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-paradise .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-paradise .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-paradise .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-paradise .TextArea{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-paradise .TextArea--fluid{display:block;width:auto;height:auto}.theme-paradise .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-paradise .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-paradise .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-paradise .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-paradise .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-paradise .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-paradise .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-paradise .Knob__popupValue,.theme-paradise .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .Knob__popupValue--right{top:.25rem;right:-50%}.theme-paradise .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-paradise .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-paradise .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-paradise .Knob__ringFillPivot{transform:rotate(135deg)}.theme-paradise .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-paradise .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-paradise .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-paradise .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-paradise .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-paradise .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-paradise .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-paradise .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-paradise .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-paradise .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-paradise .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-paradise .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-paradise .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-paradise .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-paradise .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-paradise .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-paradise .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-paradise .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--label .Knob__ringFill{stroke:#955d4b}.theme-paradise .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-paradise .Slider:not(.Slider__disabled){cursor:e-resize}.theme-paradise .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-paradise .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-paradise .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-paradise .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-paradise .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-paradise .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-paradise .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-paradise .ProgressBar--color--default{border:.0833333333em solid #1b6d6d}.theme-paradise .ProgressBar--color--default .ProgressBar__fill{background-color:#1b6d6d}.theme-paradise .ProgressBar--color--disabled{border:1px solid #999}.theme-paradise .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-paradise .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-paradise .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-paradise .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-paradise .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-paradise .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-paradise .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-paradise .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-paradise .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-paradise .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-paradise .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-paradise .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-paradise .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-paradise .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-paradise .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-paradise .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-paradise .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-paradise .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-paradise .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-paradise .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-paradise .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-paradise .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-paradise .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-paradise .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-paradise .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-paradise .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-paradise .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-paradise .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-paradise .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-paradise .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-paradise .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-paradise .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--label{border:.0833333333em solid #6d4436!important}.theme-paradise .ProgressBar--color--label .ProgressBar__fill{background-color:#6d4436}.theme-paradise .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-paradise .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-paradise .Chat{color:#abc6ec}.theme-paradise .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-paradise .Chat__badge:before{content:"x"}.theme-paradise .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-paradise .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-paradise .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-paradise .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#fff;background-color:#db2828}.theme-paradise .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-paradise .Chat__highlight{color:#000}.theme-paradise .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-paradise .ChatMessage{word-wrap:break-word}.theme-paradise .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-paradise .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-paradise html,.theme-paradise body{scrollbar-color:#cb1551 #680b29}.theme-paradise .Layout,.theme-paradise .Layout *{scrollbar-base-color:#680b29;scrollbar-face-color:#99103d;scrollbar-3dlight-color:#800d33;scrollbar-highlight-color:#800d33;scrollbar-track-color:#680b29;scrollbar-arrow-color:#ea2e6c;scrollbar-shadow-color:#99103d}.theme-paradise .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-paradise .Layout__content--flexRow{display:flex;flex-flow:row}.theme-paradise .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-paradise .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-paradise .Layout__content--noMargin{margin:0}.theme-paradise .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#800d33;background-image:linear-gradient(to bottom,#80014b,#80460d)}.theme-paradise .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-paradise .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-paradise .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-paradise .Window__contentPadding:after{height:0}.theme-paradise .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-paradise .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(166,34,78,.25);pointer-events:none}.theme-paradise .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-paradise .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-paradise .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-paradise .TitleBar{background-color:#800d33;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-paradise .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#800d33;transition:color .25s,background-color .25s}.theme-paradise .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-paradise .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-paradise .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-paradise .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-paradise .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-paradise .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-paradise .adminooc{color:#29ccbe}.theme-paradise .debug{color:#8f39e6}.theme-paradise .boxed_message{background:rgba(0,0,0,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-paradise .boxed_message.left_align_text{text-align:left}.theme-paradise .boxed_message.red_border{background:rgba(0,0,0,.25);border-color:#a00}.theme-paradise .boxed_message.green_border{background:rgba(0,0,0,.25);border-color:#0f0}.theme-paradise .boxed_message.purple_border{background:rgba(0,0,0,.25);border-color:#8000ff}.theme-paradise .boxed_message.notice_border{background:rgba(0,0,0,.25);border-color:#6685f5}.theme-paradise .boxed_message.thick_border{border-width:thick} diff --git a/tgui/public/tgui-panel.bundle.js b/tgui/public/tgui-panel.bundle.js index 2b8f414df9c9..52efaecd4135 100644 --- a/tgui/public/tgui-panel.bundle.js +++ b/tgui/public/tgui-panel.bundle.js @@ -1,8 +1,8 @@ -(function(){(function(){var xn={96376:function(E,e,t){"use strict";e.__esModule=!0,e.createPopper=void 0,e.popperGenerator=g;var n=i(t(74758)),r=i(t(28811)),o=i(t(98309)),a=i(t(44896)),u=i(t(33118)),s=i(t(10579)),c=i(t(56500)),h=i(t(17633));e.detectOverflow=h.default;var f=t(75573);function i(p){return p&&p.__esModule?p:{default:p}}var d={placement:"bottom",modifiers:[],strategy:"absolute"};function l(){for(var p=arguments.length,m=new Array(p),b=0;b0&&(0,r.round)(i.width)/c.offsetWidth||1,l=c.offsetHeight>0&&(0,r.round)(i.height)/c.offsetHeight||1);var g=(0,n.isElement)(c)?(0,o.default)(c):window,v=g.visualViewport,p=!(0,a.default)()&&f,m=(i.left+(p&&v?v.offsetLeft:0))/d,b=(i.top+(p&&v?v.offsetTop:0))/l,I=i.width/d,A=i.height/l;return{width:I,height:A,top:b,right:m+I,bottom:b+A,left:m,x:m,y:b}}},49035:function(E,e,t){"use strict";e.__esModule=!0,e.default=A;var n=t(46206),r=p(t(87991)),o=p(t(79752)),a=p(t(98309)),u=p(t(44896)),s=p(t(40600)),c=p(t(16599)),h=t(75573),f=p(t(37786)),i=p(t(57819)),d=p(t(4206)),l=p(t(12972)),g=p(t(81666)),v=t(63618);function p(C){return C&&C.__esModule?C:{default:C}}function m(C,S){var y=(0,f.default)(C,!1,S==="fixed");return y.top=y.top+C.clientTop,y.left=y.left+C.clientLeft,y.bottom=y.top+C.clientHeight,y.right=y.left+C.clientWidth,y.width=C.clientWidth,y.height=C.clientHeight,y.x=y.left,y.y=y.top,y}function b(C,S,y){return S===n.viewport?(0,g.default)((0,r.default)(C,y)):(0,h.isElement)(S)?m(S,y):(0,g.default)((0,o.default)((0,s.default)(C)))}function I(C){var S=(0,a.default)((0,i.default)(C)),y=["absolute","fixed"].indexOf((0,c.default)(C).position)>=0,T=y&&(0,h.isHTMLElement)(C)?(0,u.default)(C):C;return(0,h.isElement)(T)?S.filter(function(N){return(0,h.isElement)(N)&&(0,d.default)(N,T)&&(0,l.default)(N)!=="body"}):[]}function A(C,S,y,T){var N=S==="clippingParents"?I(C):[].concat(S),M=[].concat(N,[y]),R=M[0],L=M.reduce(function(B,x){var V=b(C,x,T);return B.top=(0,v.max)(V.top,B.top),B.right=(0,v.min)(V.right,B.right),B.bottom=(0,v.min)(V.bottom,B.bottom),B.left=(0,v.max)(V.left,B.left),B},b(C,R,T));return L.width=L.right-L.left,L.height=L.bottom-L.top,L.x=L.left,L.y=L.top,L}},74758:function(E,e,t){"use strict";e.__esModule=!0,e.default=d;var n=f(t(37786)),r=f(t(13390)),o=f(t(12972)),a=t(75573),u=f(t(79697)),s=f(t(40600)),c=f(t(10798)),h=t(63618);function f(l){return l&&l.__esModule?l:{default:l}}function i(l){var g=l.getBoundingClientRect(),v=(0,h.round)(g.width)/l.offsetWidth||1,p=(0,h.round)(g.height)/l.offsetHeight||1;return v!==1||p!==1}function d(l,g,v){v===void 0&&(v=!1);var p=(0,a.isHTMLElement)(g),m=(0,a.isHTMLElement)(g)&&i(g),b=(0,s.default)(g),I=(0,n.default)(l,m,v),A={scrollLeft:0,scrollTop:0},C={x:0,y:0};return(p||!p&&!v)&&(((0,o.default)(g)!=="body"||(0,c.default)(b))&&(A=(0,r.default)(g)),(0,a.isHTMLElement)(g)?(C=(0,n.default)(g,!0),C.x+=g.clientLeft,C.y+=g.clientTop):b&&(C.x=(0,u.default)(b))),{x:I.left+A.scrollLeft-C.x,y:I.top+A.scrollTop-C.y,width:I.width,height:I.height}}},16599:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return(0,n.default)(a).getComputedStyle(a)}},40600:function(E,e,t){"use strict";e.__esModule=!0,e.default=r;var n=t(75573);function r(o){return(((0,n.isElement)(o)?o.ownerDocument:o.document)||window.document).documentElement}},79752:function(E,e,t){"use strict";e.__esModule=!0,e.default=c;var n=s(t(40600)),r=s(t(16599)),o=s(t(79697)),a=s(t(43750)),u=t(63618);function s(h){return h&&h.__esModule?h:{default:h}}function c(h){var f,i=(0,n.default)(h),d=(0,a.default)(h),l=(f=h.ownerDocument)==null?void 0:f.body,g=(0,u.max)(i.scrollWidth,i.clientWidth,l?l.scrollWidth:0,l?l.clientWidth:0),v=(0,u.max)(i.scrollHeight,i.clientHeight,l?l.scrollHeight:0,l?l.clientHeight:0),p=-d.scrollLeft+(0,o.default)(h),m=-d.scrollTop;return(0,r.default)(l||i).direction==="rtl"&&(p+=(0,u.max)(i.clientWidth,l?l.clientWidth:0)-g),{width:g,height:v,x:p,y:m}}},3073:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){return{scrollLeft:n.scrollLeft,scrollTop:n.scrollTop}}},28811:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=r(t(37786));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var u=(0,n.default)(a),s=a.offsetWidth,c=a.offsetHeight;return Math.abs(u.width-s)<=1&&(s=u.width),Math.abs(u.height-c)<=1&&(c=u.height),{x:a.offsetLeft,y:a.offsetTop,width:s,height:c}}},12972:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){return n?(n.nodeName||"").toLowerCase():null}},13390:function(E,e,t){"use strict";e.__esModule=!0,e.default=s;var n=u(t(43750)),r=u(t(95115)),o=t(75573),a=u(t(3073));function u(c){return c&&c.__esModule?c:{default:c}}function s(c){return c===(0,r.default)(c)||!(0,o.isHTMLElement)(c)?(0,n.default)(c):(0,a.default)(c)}},44896:function(E,e,t){"use strict";e.__esModule=!0,e.default=d;var n=h(t(95115)),r=h(t(12972)),o=h(t(16599)),a=t(75573),u=h(t(87031)),s=h(t(57819)),c=h(t(35366));function h(l){return l&&l.__esModule?l:{default:l}}function f(l){return!(0,a.isHTMLElement)(l)||(0,o.default)(l).position==="fixed"?null:l.offsetParent}function i(l){var g=/firefox/i.test((0,c.default)()),v=/Trident/i.test((0,c.default)());if(v&&(0,a.isHTMLElement)(l)){var p=(0,o.default)(l);if(p.position==="fixed")return null}var m=(0,s.default)(l);for((0,a.isShadowRoot)(m)&&(m=m.host);(0,a.isHTMLElement)(m)&&["html","body"].indexOf((0,r.default)(m))<0;){var b=(0,o.default)(m);if(b.transform!=="none"||b.perspective!=="none"||b.contain==="paint"||["transform","perspective"].indexOf(b.willChange)!==-1||g&&b.willChange==="filter"||g&&b.filter&&b.filter!=="none")return m;m=m.parentNode}return null}function d(l){for(var g=(0,n.default)(l),v=f(l);v&&(0,u.default)(v)&&(0,o.default)(v).position==="static";)v=f(v);return v&&((0,r.default)(v)==="html"||(0,r.default)(v)==="body"&&(0,o.default)(v).position==="static")?g:v||i(l)||g}},57819:function(E,e,t){"use strict";e.__esModule=!0,e.default=u;var n=a(t(12972)),r=a(t(40600)),o=t(75573);function a(s){return s&&s.__esModule?s:{default:s}}function u(s){return(0,n.default)(s)==="html"?s:s.assignedSlot||s.parentNode||((0,o.isShadowRoot)(s)?s.host:null)||(0,r.default)(s)}},24426:function(E,e,t){"use strict";e.__esModule=!0,e.default=s;var n=u(t(57819)),r=u(t(10798)),o=u(t(12972)),a=t(75573);function u(c){return c&&c.__esModule?c:{default:c}}function s(c){return["html","body","#document"].indexOf((0,o.default)(c))>=0?c.ownerDocument.body:(0,a.isHTMLElement)(c)&&(0,r.default)(c)?c:s((0,n.default)(c))}},87991:function(E,e,t){"use strict";e.__esModule=!0,e.default=s;var n=u(t(95115)),r=u(t(40600)),o=u(t(79697)),a=u(t(89331));function u(c){return c&&c.__esModule?c:{default:c}}function s(c,h){var f=(0,n.default)(c),i=(0,r.default)(c),d=f.visualViewport,l=i.clientWidth,g=i.clientHeight,v=0,p=0;if(d){l=d.width,g=d.height;var m=(0,a.default)();(m||!m&&h==="fixed")&&(v=d.offsetLeft,p=d.offsetTop)}return{width:l,height:g,x:v+(0,o.default)(c),y:p}}},95115:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){if(n==null)return window;if(n.toString()!=="[object Window]"){var r=n.ownerDocument;return r&&r.defaultView||window}return n}},43750:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var u=(0,n.default)(a),s=u.pageXOffset,c=u.pageYOffset;return{scrollLeft:s,scrollTop:c}}},79697:function(E,e,t){"use strict";e.__esModule=!0,e.default=u;var n=a(t(37786)),r=a(t(40600)),o=a(t(43750));function a(s){return s&&s.__esModule?s:{default:s}}function u(s){return(0,n.default)((0,r.default)(s)).left+(0,o.default)(s).scrollLeft}},75573:function(E,e,t){"use strict";e.__esModule=!0,e.isElement=o,e.isHTMLElement=a,e.isShadowRoot=u;var n=r(t(95115));function r(s){return s&&s.__esModule?s:{default:s}}function o(s){var c=(0,n.default)(s).Element;return s instanceof c||s instanceof Element}function a(s){var c=(0,n.default)(s).HTMLElement;return s instanceof c||s instanceof HTMLElement}function u(s){if(typeof ShadowRoot=="undefined")return!1;var c=(0,n.default)(s).ShadowRoot;return s instanceof c||s instanceof ShadowRoot}},89331:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=r(t(35366));function r(a){return a&&a.__esModule?a:{default:a}}function o(){return!/^((?!chrome|android).)*safari/i.test((0,n.default)())}},10798:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=r(t(16599));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var u=(0,n.default)(a),s=u.overflow,c=u.overflowX,h=u.overflowY;return/auto|scroll|overlay|hidden/.test(s+h+c)}},87031:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=r(t(12972));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return["table","td","th"].indexOf((0,n.default)(a))>=0}},98309:function(E,e,t){"use strict";e.__esModule=!0,e.default=s;var n=u(t(24426)),r=u(t(57819)),o=u(t(95115)),a=u(t(10798));function u(c){return c&&c.__esModule?c:{default:c}}function s(c,h){var f;h===void 0&&(h=[]);var i=(0,n.default)(c),d=i===((f=c.ownerDocument)==null?void 0:f.body),l=(0,o.default)(i),g=d?[l].concat(l.visualViewport||[],(0,a.default)(i)?i:[]):i,v=h.concat(g);return d?v:v.concat(s((0,r.default)(g)))}},46206:function(E,e){"use strict";e.__esModule=!0,e.write=e.viewport=e.variationPlacements=e.top=e.start=e.right=e.reference=e.read=e.popper=e.placements=e.modifierPhases=e.main=e.left=e.end=e.clippingParents=e.bottom=e.beforeWrite=e.beforeRead=e.beforeMain=e.basePlacements=e.auto=e.afterWrite=e.afterRead=e.afterMain=void 0;var t=e.top="top",n=e.bottom="bottom",r=e.right="right",o=e.left="left",a=e.auto="auto",u=e.basePlacements=[t,n,r,o],s=e.start="start",c=e.end="end",h=e.clippingParents="clippingParents",f=e.viewport="viewport",i=e.popper="popper",d=e.reference="reference",l=e.variationPlacements=u.reduce(function(N,M){return N.concat([M+"-"+s,M+"-"+c])},[]),g=e.placements=[].concat(u,[a]).reduce(function(N,M){return N.concat([M,M+"-"+s,M+"-"+c])},[]),v=e.beforeRead="beforeRead",p=e.read="read",m=e.afterRead="afterRead",b=e.beforeMain="beforeMain",I=e.main="main",A=e.afterMain="afterMain",C=e.beforeWrite="beforeWrite",S=e.write="write",y=e.afterWrite="afterWrite",T=e.modifierPhases=[v,p,m,b,I,A,C,S,y]},95996:function(E,e,t){"use strict";e.__esModule=!0;var n={popperGenerator:!0,detectOverflow:!0,createPopperBase:!0,createPopper:!0,createPopperLite:!0};e.popperGenerator=e.detectOverflow=e.createPopperLite=e.createPopperBase=e.createPopper=void 0;var r=t(46206);Object.keys(r).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(n,c)||c in e&&e[c]===r[c]||(e[c]=r[c])});var o=t(39805);Object.keys(o).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(n,c)||c in e&&e[c]===o[c]||(e[c]=o[c])});var a=t(96376);e.popperGenerator=a.popperGenerator,e.detectOverflow=a.detectOverflow,e.createPopperBase=a.createPopper;var u=t(83312);e.createPopper=u.createPopper;var s=t(2473);e.createPopperLite=s.createPopper},19975:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0;var n=o(t(12972)),r=t(75573);function o(c){return c&&c.__esModule?c:{default:c}}function a(c){var h=c.state;Object.keys(h.elements).forEach(function(f){var i=h.styles[f]||{},d=h.attributes[f]||{},l=h.elements[f];!(0,r.isHTMLElement)(l)||!(0,n.default)(l)||(Object.assign(l.style,i),Object.keys(d).forEach(function(g){var v=d[g];v===!1?l.removeAttribute(g):l.setAttribute(g,v===!0?"":v)}))})}function u(c){var h=c.state,f={popper:{position:h.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(h.elements.popper.style,f.popper),h.styles=f,h.elements.arrow&&Object.assign(h.elements.arrow.style,f.arrow),function(){Object.keys(h.elements).forEach(function(i){var d=h.elements[i],l=h.attributes[i]||{},g=Object.keys(h.styles.hasOwnProperty(i)?h.styles[i]:f[i]),v=g.reduce(function(p,m){return p[m]="",p},{});!(0,r.isHTMLElement)(d)||!(0,n.default)(d)||(Object.assign(d.style,v),Object.keys(l).forEach(function(p){d.removeAttribute(p)}))})}}var s=e.default={name:"applyStyles",enabled:!0,phase:"write",fn:a,effect:u,requires:["computeStyles"]}},52744:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0;var n=i(t(83104)),r=i(t(28811)),o=i(t(4206)),a=i(t(44896)),u=i(t(41199)),s=t(28595),c=i(t(43286)),h=i(t(81447)),f=t(46206);function i(p){return p&&p.__esModule?p:{default:p}}var d=function(){function p(m,b){return m=typeof m=="function"?m(Object.assign({},b.rects,{placement:b.placement})):m,(0,c.default)(typeof m!="number"?m:(0,h.default)(m,f.basePlacements))}return p}();function l(p){var m,b=p.state,I=p.name,A=p.options,C=b.elements.arrow,S=b.modifiersData.popperOffsets,y=(0,n.default)(b.placement),T=(0,u.default)(y),N=[f.left,f.right].indexOf(y)>=0,M=N?"height":"width";if(!(!C||!S)){var R=d(A.padding,b),L=(0,r.default)(C),B=T==="y"?f.top:f.left,x=T==="y"?f.bottom:f.right,V=b.rects.reference[M]+b.rects.reference[T]-S[T]-b.rects.popper[M],j=S[T]-b.rects.reference[T],Y=(0,a.default)(C),D=Y?T==="y"?Y.clientHeight||0:Y.clientWidth||0:0,U=V/2-j/2,G=R[B],K=D-L[M]-R[x],$=D/2-L[M]/2+U,W=(0,s.within)(G,$,K),tt=T;b.modifiersData[I]=(m={},m[tt]=W,m.centerOffset=W-$,m)}}function g(p){var m=p.state,b=p.options,I=b.element,A=I===void 0?"[data-popper-arrow]":I;A!=null&&(typeof A=="string"&&(A=m.elements.popper.querySelector(A),!A)||(0,o.default)(m.elements.popper,A)&&(m.elements.arrow=A))}var v=e.default={name:"arrow",enabled:!0,phase:"main",fn:l,effect:g,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]}},59894:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0,e.mapToStyles=l;var n=t(46206),r=f(t(44896)),o=f(t(95115)),a=f(t(40600)),u=f(t(16599)),s=f(t(83104)),c=f(t(45)),h=t(63618);function f(p){return p&&p.__esModule?p:{default:p}}var i={top:"auto",right:"auto",bottom:"auto",left:"auto"};function d(p,m){var b=p.x,I=p.y,A=m.devicePixelRatio||1;return{x:(0,h.round)(b*A)/A||0,y:(0,h.round)(I*A)/A||0}}function l(p){var m,b=p.popper,I=p.popperRect,A=p.placement,C=p.variation,S=p.offsets,y=p.position,T=p.gpuAcceleration,N=p.adaptive,M=p.roundOffsets,R=p.isFixed,L=S.x,B=L===void 0?0:L,x=S.y,V=x===void 0?0:x,j=typeof M=="function"?M({x:B,y:V}):{x:B,y:V};B=j.x,V=j.y;var Y=S.hasOwnProperty("x"),D=S.hasOwnProperty("y"),U=n.left,G=n.top,K=window;if(N){var $=(0,r.default)(b),W="clientHeight",tt="clientWidth";if($===(0,o.default)(b)&&($=(0,a.default)(b),(0,u.default)($).position!=="static"&&y==="absolute"&&(W="scrollHeight",tt="scrollWidth")),$=$,A===n.top||(A===n.left||A===n.right)&&C===n.end){G=n.bottom;var ut=R&&$===K&&K.visualViewport?K.visualViewport.height:$[W];V-=ut-I.height,V*=T?1:-1}if(A===n.left||(A===n.top||A===n.bottom)&&C===n.end){U=n.right;var ht=R&&$===K&&K.visualViewport?K.visualViewport.width:$[tt];B-=ht-I.width,B*=T?1:-1}}var k=Object.assign({position:y},N&&i),Z=M===!0?d({x:B,y:V},(0,o.default)(b)):{x:B,y:V};if(B=Z.x,V=Z.y,T){var nt;return Object.assign({},k,(nt={},nt[G]=D?"0":"",nt[U]=Y?"0":"",nt.transform=(K.devicePixelRatio||1)<=1?"translate("+B+"px, "+V+"px)":"translate3d("+B+"px, "+V+"px, 0)",nt))}return Object.assign({},k,(m={},m[G]=D?V+"px":"",m[U]=Y?B+"px":"",m.transform="",m))}function g(p){var m=p.state,b=p.options,I=b.gpuAcceleration,A=I===void 0?!0:I,C=b.adaptive,S=C===void 0?!0:C,y=b.roundOffsets,T=y===void 0?!0:y,N={placement:(0,s.default)(m.placement),variation:(0,c.default)(m.placement),popper:m.elements.popper,popperRect:m.rects.popper,gpuAcceleration:A,isFixed:m.options.strategy==="fixed"};m.modifiersData.popperOffsets!=null&&(m.styles.popper=Object.assign({},m.styles.popper,l(Object.assign({},N,{offsets:m.modifiersData.popperOffsets,position:m.options.strategy,adaptive:S,roundOffsets:T})))),m.modifiersData.arrow!=null&&(m.styles.arrow=Object.assign({},m.styles.arrow,l(Object.assign({},N,{offsets:m.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:T})))),m.attributes.popper=Object.assign({},m.attributes.popper,{"data-popper-placement":m.placement})}var v=e.default={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:g,data:{}}},36692:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0;var n=r(t(95115));function r(s){return s&&s.__esModule?s:{default:s}}var o={passive:!0};function a(s){var c=s.state,h=s.instance,f=s.options,i=f.scroll,d=i===void 0?!0:i,l=f.resize,g=l===void 0?!0:l,v=(0,n.default)(c.elements.popper),p=[].concat(c.scrollParents.reference,c.scrollParents.popper);return d&&p.forEach(function(m){m.addEventListener("scroll",h.update,o)}),g&&v.addEventListener("resize",h.update,o),function(){d&&p.forEach(function(m){m.removeEventListener("scroll",h.update,o)}),g&&v.removeEventListener("resize",h.update,o)}}var u=e.default={name:"eventListeners",enabled:!0,phase:"write",fn:function(){function s(){}return s}(),effect:a,data:{}}},23798:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0;var n=h(t(71376)),r=h(t(83104)),o=h(t(86459)),a=h(t(17633)),u=h(t(9041)),s=t(46206),c=h(t(45));function h(l){return l&&l.__esModule?l:{default:l}}function f(l){if((0,r.default)(l)===s.auto)return[];var g=(0,n.default)(l);return[(0,o.default)(l),g,(0,o.default)(g)]}function i(l){var g=l.state,v=l.options,p=l.name;if(!g.modifiersData[p]._skip){for(var m=v.mainAxis,b=m===void 0?!0:m,I=v.altAxis,A=I===void 0?!0:I,C=v.fallbackPlacements,S=v.padding,y=v.boundary,T=v.rootBoundary,N=v.altBoundary,M=v.flipVariations,R=M===void 0?!0:M,L=v.allowedAutoPlacements,B=g.options.placement,x=(0,r.default)(B),V=x===B,j=C||(V||!R?[(0,n.default)(B)]:f(B)),Y=[B].concat(j).reduce(function(rt,X){return rt.concat((0,r.default)(X)===s.auto?(0,u.default)(g,{placement:X,boundary:y,rootBoundary:T,padding:S,flipVariations:R,allowedAutoPlacements:L}):X)},[]),D=g.rects.reference,U=g.rects.popper,G=new Map,K=!0,$=Y[0],W=0;W=0,Z=k?"width":"height",nt=(0,a.default)(g,{placement:tt,boundary:y,rootBoundary:T,altBoundary:N,padding:S}),st=k?ht?s.right:s.left:ht?s.bottom:s.top;D[Z]>U[Z]&&(st=(0,n.default)(st));var ot=(0,n.default)(st),pt=[];if(b&&pt.push(nt[ut]<=0),A&&pt.push(nt[st]<=0,nt[ot]<=0),pt.every(function(rt){return rt})){$=tt,K=!1;break}G.set(tt,pt)}if(K)for(var Ot=R?3:1,Nt=function(){function rt(X){var J=Y.find(function(et){var ft=G.get(et);if(ft)return ft.slice(0,X).every(function(mt){return mt})});if(J)return $=J,"break"}return rt}(),Pt=Ot;Pt>0;Pt--){var dt=Nt(Pt);if(dt==="break")break}g.placement!==$&&(g.modifiersData[p]._skip=!0,g.placement=$,g.reset=!0)}}var d=e.default={name:"flip",enabled:!0,phase:"main",fn:i,requiresIfExists:["offset"],data:{_skip:!1}}},83761:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0;var n=t(46206),r=o(t(17633));function o(h){return h&&h.__esModule?h:{default:h}}function a(h,f,i){return i===void 0&&(i={x:0,y:0}),{top:h.top-f.height-i.y,right:h.right-f.width+i.x,bottom:h.bottom-f.height+i.y,left:h.left-f.width-i.x}}function u(h){return[n.top,n.right,n.bottom,n.left].some(function(f){return h[f]>=0})}function s(h){var f=h.state,i=h.name,d=f.rects.reference,l=f.rects.popper,g=f.modifiersData.preventOverflow,v=(0,r.default)(f,{elementContext:"reference"}),p=(0,r.default)(f,{altBoundary:!0}),m=a(v,d),b=a(p,l,g),I=u(m),A=u(b);f.modifiersData[i]={referenceClippingOffsets:m,popperEscapeOffsets:b,isReferenceHidden:I,hasPopperEscaped:A},f.attributes.popper=Object.assign({},f.attributes.popper,{"data-popper-reference-hidden":I,"data-popper-escaped":A})}var c=e.default={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:s}},39805:function(E,e,t){"use strict";e.__esModule=!0,e.preventOverflow=e.popperOffsets=e.offset=e.hide=e.flip=e.eventListeners=e.computeStyles=e.arrow=e.applyStyles=void 0;var n=i(t(19975));e.applyStyles=n.default;var r=i(t(52744));e.arrow=r.default;var o=i(t(59894));e.computeStyles=o.default;var a=i(t(36692));e.eventListeners=a.default;var u=i(t(23798));e.flip=u.default;var s=i(t(83761));e.hide=s.default;var c=i(t(61410));e.offset=c.default;var h=i(t(40107));e.popperOffsets=h.default;var f=i(t(75137));e.preventOverflow=f.default;function i(d){return d&&d.__esModule?d:{default:d}}},61410:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0,e.distanceAndSkiddingToXY=a;var n=o(t(83104)),r=t(46206);function o(c){return c&&c.__esModule?c:{default:c}}function a(c,h,f){var i=(0,n.default)(c),d=[r.left,r.top].indexOf(i)>=0?-1:1,l=typeof f=="function"?f(Object.assign({},h,{placement:c})):f,g=l[0],v=l[1];return g=g||0,v=(v||0)*d,[r.left,r.right].indexOf(i)>=0?{x:v,y:g}:{x:g,y:v}}function u(c){var h=c.state,f=c.options,i=c.name,d=f.offset,l=d===void 0?[0,0]:d,g=r.placements.reduce(function(b,I){return b[I]=a(I,h.rects,l),b},{}),v=g[h.placement],p=v.x,m=v.y;h.modifiersData.popperOffsets!=null&&(h.modifiersData.popperOffsets.x+=p,h.modifiersData.popperOffsets.y+=m),h.modifiersData[i]=g}var s=e.default={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:u}},40107:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0;var n=r(t(89951));function r(u){return u&&u.__esModule?u:{default:u}}function o(u){var s=u.state,c=u.name;s.modifiersData[c]=(0,n.default)({reference:s.rects.reference,element:s.rects.popper,strategy:"absolute",placement:s.placement})}var a=e.default={name:"popperOffsets",enabled:!0,phase:"read",fn:o,data:{}}},75137:function(E,e,t){"use strict";e.__esModule=!0,e.default=void 0;var n=t(46206),r=l(t(83104)),o=l(t(41199)),a=l(t(28066)),u=t(28595),s=l(t(28811)),c=l(t(44896)),h=l(t(17633)),f=l(t(45)),i=l(t(34780)),d=t(63618);function l(p){return p&&p.__esModule?p:{default:p}}function g(p){var m=p.state,b=p.options,I=p.name,A=b.mainAxis,C=A===void 0?!0:A,S=b.altAxis,y=S===void 0?!1:S,T=b.boundary,N=b.rootBoundary,M=b.altBoundary,R=b.padding,L=b.tether,B=L===void 0?!0:L,x=b.tetherOffset,V=x===void 0?0:x,j=(0,h.default)(m,{boundary:T,rootBoundary:N,padding:R,altBoundary:M}),Y=(0,r.default)(m.placement),D=(0,f.default)(m.placement),U=!D,G=(0,o.default)(Y),K=(0,a.default)(G),$=m.modifiersData.popperOffsets,W=m.rects.reference,tt=m.rects.popper,ut=typeof V=="function"?V(Object.assign({},m.rects,{placement:m.placement})):V,ht=typeof ut=="number"?{mainAxis:ut,altAxis:ut}:Object.assign({mainAxis:0,altAxis:0},ut),k=m.modifiersData.offset?m.modifiersData.offset[m.placement]:null,Z={x:0,y:0};if($){if(C){var nt,st=G==="y"?n.top:n.left,ot=G==="y"?n.bottom:n.right,pt=G==="y"?"height":"width",Ot=$[G],Nt=Ot+j[st],Pt=Ot-j[ot],dt=B?-tt[pt]/2:0,rt=D===n.start?W[pt]:tt[pt],X=D===n.start?-tt[pt]:-W[pt],J=m.elements.arrow,et=B&&J?(0,s.default)(J):{width:0,height:0},ft=m.modifiersData["arrow#persistent"]?m.modifiersData["arrow#persistent"].padding:(0,i.default)(),mt=ft[st],at=ft[ot],vt=(0,u.within)(0,W[pt],et[pt]),It=U?W[pt]/2-dt-vt-mt-ht.mainAxis:rt-vt-mt-ht.mainAxis,Q=U?-W[pt]/2+dt+vt+at+ht.mainAxis:X+vt+at+ht.mainAxis,ct=m.elements.arrow&&(0,c.default)(m.elements.arrow),yt=ct?G==="y"?ct.clientTop||0:ct.clientLeft||0:0,Tt=(nt=k==null?void 0:k[G])!=null?nt:0,Dt=Ot+It-Tt-yt,jt=Ot+Q-Tt,Ct=(0,u.within)(B?(0,d.min)(Nt,Dt):Nt,Ot,B?(0,d.max)(Pt,jt):Pt);$[G]=Ct,Z[G]=Ct-Ot}if(y){var lt,gt=G==="x"?n.top:n.left,bt=G==="x"?n.bottom:n.right,St=$[K],At=K==="y"?"height":"width",Ft=St+j[gt],Vt=St-j[bt],Gt=[n.top,n.left].indexOf(Y)!==-1,Ht=(lt=k==null?void 0:k[K])!=null?lt:0,Yt=Gt?Ft:St-W[At]-tt[At]-Ht+ht.altAxis,Wt=Gt?St+W[At]+tt[At]-Ht-ht.altAxis:Vt,Zt=B&&Gt?(0,u.withinMaxClamp)(Yt,St,Wt):(0,u.within)(B?Yt:Ft,St,B?Wt:Vt);$[K]=Zt,Z[K]=Zt-St}m.modifiersData[I]=Z}}var v=e.default={name:"preventOverflow",enabled:!0,phase:"main",fn:g,requiresIfExists:["offset"]}},2473:function(E,e,t){"use strict";e.__esModule=!0,e.defaultModifiers=e.createPopper=void 0;var n=t(96376);e.popperGenerator=n.popperGenerator,e.detectOverflow=n.detectOverflow;var r=s(t(36692)),o=s(t(40107)),a=s(t(59894)),u=s(t(19975));function s(f){return f&&f.__esModule?f:{default:f}}var c=e.defaultModifiers=[r.default,o.default,a.default,u.default],h=e.createPopper=(0,n.popperGenerator)({defaultModifiers:c})},83312:function(E,e,t){"use strict";e.__esModule=!0;var n={createPopper:!0,createPopperLite:!0,defaultModifiers:!0,popperGenerator:!0,detectOverflow:!0};e.defaultModifiers=e.createPopperLite=e.createPopper=void 0;var r=t(96376);e.popperGenerator=r.popperGenerator,e.detectOverflow=r.detectOverflow;var o=v(t(36692)),a=v(t(40107)),u=v(t(59894)),s=v(t(19975)),c=v(t(61410)),h=v(t(23798)),f=v(t(75137)),i=v(t(52744)),d=v(t(83761)),l=t(2473);e.createPopperLite=l.createPopper;var g=t(39805);Object.keys(g).forEach(function(b){b==="default"||b==="__esModule"||Object.prototype.hasOwnProperty.call(n,b)||b in e&&e[b]===g[b]||(e[b]=g[b])});function v(b){return b&&b.__esModule?b:{default:b}}var p=e.defaultModifiers=[o.default,a.default,u.default,s.default,c.default,h.default,f.default,i.default,d.default],m=e.createPopperLite=e.createPopper=(0,r.popperGenerator)({defaultModifiers:p})},9041:function(E,e,t){"use strict";e.__esModule=!0,e.default=s;var n=u(t(45)),r=t(46206),o=u(t(17633)),a=u(t(83104));function u(c){return c&&c.__esModule?c:{default:c}}function s(c,h){h===void 0&&(h={});var f=h,i=f.placement,d=f.boundary,l=f.rootBoundary,g=f.padding,v=f.flipVariations,p=f.allowedAutoPlacements,m=p===void 0?r.placements:p,b=(0,n.default)(i),I=b?v?r.variationPlacements:r.variationPlacements.filter(function(S){return(0,n.default)(S)===b}):r.basePlacements,A=I.filter(function(S){return m.indexOf(S)>=0});A.length===0&&(A=I);var C=A.reduce(function(S,y){return S[y]=(0,o.default)(c,{placement:y,boundary:d,rootBoundary:l,padding:g})[(0,a.default)(y)],S},{});return Object.keys(C).sort(function(S,y){return C[S]-C[y]})}},89951:function(E,e,t){"use strict";e.__esModule=!0,e.default=s;var n=u(t(83104)),r=u(t(45)),o=u(t(41199)),a=t(46206);function u(c){return c&&c.__esModule?c:{default:c}}function s(c){var h=c.reference,f=c.element,i=c.placement,d=i?(0,n.default)(i):null,l=i?(0,r.default)(i):null,g=h.x+h.width/2-f.width/2,v=h.y+h.height/2-f.height/2,p;switch(d){case a.top:p={x:g,y:h.y-f.height};break;case a.bottom:p={x:g,y:h.y+h.height};break;case a.right:p={x:h.x+h.width,y:v};break;case a.left:p={x:h.x-f.width,y:v};break;default:p={x:h.x,y:h.y}}var m=d?(0,o.default)(d):null;if(m!=null){var b=m==="y"?"height":"width";switch(l){case a.start:p[m]=p[m]-(h[b]/2-f[b]/2);break;case a.end:p[m]=p[m]+(h[b]/2-f[b]/2);break;default:}}return p}},10579:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){var r;return function(){return r||(r=new Promise(function(o){Promise.resolve().then(function(){r=void 0,o(n())})})),r}}},17633:function(E,e,t){"use strict";e.__esModule=!0,e.default=d;var n=i(t(49035)),r=i(t(40600)),o=i(t(37786)),a=i(t(89951)),u=i(t(81666)),s=t(46206),c=t(75573),h=i(t(43286)),f=i(t(81447));function i(l){return l&&l.__esModule?l:{default:l}}function d(l,g){g===void 0&&(g={});var v=g,p=v.placement,m=p===void 0?l.placement:p,b=v.strategy,I=b===void 0?l.strategy:b,A=v.boundary,C=A===void 0?s.clippingParents:A,S=v.rootBoundary,y=S===void 0?s.viewport:S,T=v.elementContext,N=T===void 0?s.popper:T,M=v.altBoundary,R=M===void 0?!1:M,L=v.padding,B=L===void 0?0:L,x=(0,h.default)(typeof B!="number"?B:(0,f.default)(B,s.basePlacements)),V=N===s.popper?s.reference:s.popper,j=l.rects.popper,Y=l.elements[R?V:N],D=(0,n.default)((0,c.isElement)(Y)?Y:Y.contextElement||(0,r.default)(l.elements.popper),C,y,I),U=(0,o.default)(l.elements.reference),G=(0,a.default)({reference:U,element:j,strategy:"absolute",placement:m}),K=(0,u.default)(Object.assign({},j,G)),$=N===s.popper?K:U,W={top:D.top-$.top+x.top,bottom:$.bottom-D.bottom+x.bottom,left:D.left-$.left+x.left,right:$.right-D.right+x.right},tt=l.modifiersData.offset;if(N===s.popper&&tt){var ut=tt[m];Object.keys(W).forEach(function(ht){var k=[s.right,s.bottom].indexOf(ht)>=0?1:-1,Z=[s.top,s.bottom].indexOf(ht)>=0?"y":"x";W[ht]+=ut[Z]*k})}return W}},81447:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n,r){return r.reduce(function(o,a){return o[a]=n,o},{})}},28066:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){return n==="x"?"y":"x"}},83104:function(E,e,t){"use strict";e.__esModule=!0,e.default=r;var n=t(46206);function r(o){return o.split("-")[0]}},34780:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(){return{top:0,right:0,bottom:0,left:0}}},41199:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){return["top","bottom"].indexOf(n)>=0?"x":"y"}},71376:function(E,e){"use strict";e.__esModule=!0,e.default=n;var t={left:"right",right:"left",bottom:"top",top:"bottom"};function n(r){return r.replace(/left|right|bottom|top/g,function(o){return t[o]})}},86459:function(E,e){"use strict";e.__esModule=!0,e.default=n;var t={start:"end",end:"start"};function n(r){return r.replace(/start|end/g,function(o){return t[o]})}},45:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){return n.split("-")[1]}},63618:function(E,e){"use strict";e.__esModule=!0,e.round=e.min=e.max=void 0;var t=e.max=Math.max,n=e.min=Math.min,r=e.round=Math.round},56500:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){var r=n.reduce(function(o,a){var u=o[a.name];return o[a.name]=u?Object.assign({},u,a,{options:Object.assign({},u.options,a.options),data:Object.assign({},u.data,a.data)}):a,o},{});return Object.keys(r).map(function(o){return r[o]})}},43286:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=r(t(34780));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return Object.assign({},(0,n.default)(),a)}},33118:function(E,e,t){"use strict";e.__esModule=!0,e.default=o;var n=t(46206);function r(a){var u=new Map,s=new Set,c=[];a.forEach(function(f){u.set(f.name,f)});function h(f){s.add(f.name);var i=[].concat(f.requires||[],f.requiresIfExists||[]);i.forEach(function(d){if(!s.has(d)){var l=u.get(d);l&&h(l)}}),c.push(f)}return a.forEach(function(f){s.has(f.name)||h(f)}),c}function o(a){var u=r(a);return n.modifierPhases.reduce(function(s,c){return s.concat(u.filter(function(h){return h.phase===c}))},[])}},81666:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(n){return Object.assign({},n,{left:n.x,top:n.y,right:n.x+n.width,bottom:n.y+n.height})}},35366:function(E,e){"use strict";e.__esModule=!0,e.default=t;function t(){var n=navigator.userAgentData;return n!=null&&n.brands&&Array.isArray(n.brands)?n.brands.map(function(r){return r.brand+"/"+r.version}).join(" "):navigator.userAgent}},28595:function(E,e,t){"use strict";e.__esModule=!0,e.within=r,e.withinMaxClamp=o;var n=t(63618);function r(a,u,s){return(0,n.max)(a,(0,n.min)(u,s))}function o(a,u,s){var c=r(a,u,s);return c>s?s:c}},22734:function(E){"use strict";/*! @license DOMPurify 2.5.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.0/LICENSE */(function(e,t){E.exports=t()})(void 0,function(){"use strict";function e(Q){"@babel/helpers - typeof";return e=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(ct){return typeof ct}:function(ct){return ct&&typeof Symbol=="function"&&ct.constructor===Symbol&&ct!==Symbol.prototype?"symbol":typeof ct},e(Q)}function t(Q,ct){return t=Object.setPrototypeOf||function(){function yt(Tt,Dt){return Tt.__proto__=Dt,Tt}return yt}(),t(Q,ct)}function n(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(Q){return!1}}function r(Q,ct,yt){return n()?r=Reflect.construct:r=function(){function Tt(Dt,jt,Ct){var lt=[null];lt.push.apply(lt,jt);var gt=Function.bind.apply(Dt,lt),bt=new gt;return Ct&&t(bt,Ct.prototype),bt}return Tt}(),r.apply(null,arguments)}function o(Q){return a(Q)||u(Q)||s(Q)||h()}function a(Q){if(Array.isArray(Q))return c(Q)}function u(Q){if(typeof Symbol!="undefined"&&Q[Symbol.iterator]!=null||Q["@@iterator"]!=null)return Array.from(Q)}function s(Q,ct){if(Q){if(typeof Q=="string")return c(Q,ct);var yt=Object.prototype.toString.call(Q).slice(8,-1);if(yt==="Object"&&Q.constructor&&(yt=Q.constructor.name),yt==="Map"||yt==="Set")return Array.from(Q);if(yt==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(yt))return c(Q,ct)}}function c(Q,ct){(ct==null||ct>Q.length)&&(ct=Q.length);for(var yt=0,Tt=new Array(ct);yt1?yt-1:0),Dt=1;Dt/gm),Nt=p(/\${[\w\W]*}/gm),Pt=p(/^data-[\-\w.\u00B7-\uFFFF]/),dt=p(/^aria-[\-\w]+$/),rt=p(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),X=p(/^(?:\w+script|data):/i),J=p(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),et=p(/^html$/i),ft=p(/^[a-z][.\w]*(-[.\w]+)+$/i),mt=function(){function Q(){return typeof window=="undefined"?null:window}return Q}(),at=function(){function Q(ct,yt){if(e(ct)!=="object"||typeof ct.createPolicy!="function")return null;var Tt=null,Dt="data-tt-policy-suffix";yt.currentScript&&yt.currentScript.hasAttribute(Dt)&&(Tt=yt.currentScript.getAttribute(Dt));var jt="dompurify"+(Tt?"#"+Tt:"");try{return ct.createPolicy(jt,{createHTML:function(){function Ct(lt){return lt}return Ct}(),createScriptURL:function(){function Ct(lt){return lt}return Ct}()})}catch(Ct){return null}}return Q}();function vt(){var Q=arguments.length>0&&arguments[0]!==void 0?arguments[0]:mt(),ct=function(){function O(P){return vt(P)}return O}();if(ct.version="2.5.0",ct.removed=[],!Q||!Q.document||Q.document.nodeType!==9)return ct.isSupported=!1,ct;var yt=Q.document,Tt=Q.document,Dt=Q.DocumentFragment,jt=Q.HTMLTemplateElement,Ct=Q.Node,lt=Q.Element,gt=Q.NodeFilter,bt=Q.NamedNodeMap,St=bt===void 0?Q.NamedNodeMap||Q.MozNamedAttrMap:bt,At=Q.HTMLFormElement,Ft=Q.DOMParser,Vt=Q.trustedTypes,Gt=lt.prototype,Ht=G(Gt,"cloneNode"),Yt=G(Gt,"nextSibling"),Wt=G(Gt,"childNodes"),Zt=G(Gt,"parentNode");if(typeof jt=="function"){var Le=Tt.createElement("template");Le.content&&Le.content.ownerDocument&&(Tt=Le.content.ownerDocument)}var _t=at(Vt,yt),Pe=_t?_t.createHTML(""):"",Ne=Tt,me=Ne.implementation,ye=Ne.createNodeIterator,an=Ne.createDocumentFragment,un=Ne.getElementsByTagName,Tn=yt.importNode,Ke={};try{Ke=U(Tt).documentMode?Tt.documentMode:{}}catch(O){}var re={};ct.isSupported=typeof Zt=="function"&&me&&me.createHTMLDocument!==void 0&&Ke!==9;var $e=pt,We=Ot,Be=Nt,sn=Pt,In=dt,cn=X,ln=J,An=ft,Se=rt,zt=null,te=D({},[].concat(o(K),o($),o(W),o(ut),o(k))),Kt=null,Ee=D({},[].concat(o(Z),o(nt),o(st),o(ot))),kt=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),le=null,Ce=null,He=!0,ze=!0,fn=!1,dn=!0,be=!1,De=!0,fe=!1,Fe=!1,xe=!1,de=!1,Xt=!1,Ve=!1,vn=!0,ke=!1,hn="user-content-",ue=!0,Me=!1,Te={},Ie=null,Xe=D({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),Je=null,gn=D({},["audio","video","img","source","image","track"]),je=null,pn=D({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),ee="http://www.w3.org/1998/Math/MathML",Ue="http://www.w3.org/2000/svg",se="http://www.w3.org/1999/xhtml",Ae=se,Qe=!1,Ze=null,On=D({},[ee,Ue,se],N),ce,Pn=["application/xhtml+xml","text/html"],mn="text/html",Jt,Oe=null,Nn=Tt.createElement("form"),yn=function(){function O(P){return P instanceof RegExp||P instanceof Function}return O}(),qe=function(){function O(P){Oe&&Oe===P||((!P||e(P)!=="object")&&(P={}),P=U(P),ce=Pn.indexOf(P.PARSER_MEDIA_TYPE)===-1?ce=mn:ce=P.PARSER_MEDIA_TYPE,Jt=ce==="application/xhtml+xml"?N:T,zt="ALLOWED_TAGS"in P?D({},P.ALLOWED_TAGS,Jt):te,Kt="ALLOWED_ATTR"in P?D({},P.ALLOWED_ATTR,Jt):Ee,Ze="ALLOWED_NAMESPACES"in P?D({},P.ALLOWED_NAMESPACES,N):On,je="ADD_URI_SAFE_ATTR"in P?D(U(pn),P.ADD_URI_SAFE_ATTR,Jt):pn,Je="ADD_DATA_URI_TAGS"in P?D(U(gn),P.ADD_DATA_URI_TAGS,Jt):gn,Ie="FORBID_CONTENTS"in P?D({},P.FORBID_CONTENTS,Jt):Xe,le="FORBID_TAGS"in P?D({},P.FORBID_TAGS,Jt):{},Ce="FORBID_ATTR"in P?D({},P.FORBID_ATTR,Jt):{},Te="USE_PROFILES"in P?P.USE_PROFILES:!1,He=P.ALLOW_ARIA_ATTR!==!1,ze=P.ALLOW_DATA_ATTR!==!1,fn=P.ALLOW_UNKNOWN_PROTOCOLS||!1,dn=P.ALLOW_SELF_CLOSE_IN_ATTR!==!1,be=P.SAFE_FOR_TEMPLATES||!1,De=P.SAFE_FOR_XML!==!1,fe=P.WHOLE_DOCUMENT||!1,de=P.RETURN_DOM||!1,Xt=P.RETURN_DOM_FRAGMENT||!1,Ve=P.RETURN_TRUSTED_TYPE||!1,xe=P.FORCE_BODY||!1,vn=P.SANITIZE_DOM!==!1,ke=P.SANITIZE_NAMED_PROPS||!1,ue=P.KEEP_CONTENT!==!1,Me=P.IN_PLACE||!1,Se=P.ALLOWED_URI_REGEXP||Se,Ae=P.NAMESPACE||se,kt=P.CUSTOM_ELEMENT_HANDLING||{},P.CUSTOM_ELEMENT_HANDLING&&yn(P.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(kt.tagNameCheck=P.CUSTOM_ELEMENT_HANDLING.tagNameCheck),P.CUSTOM_ELEMENT_HANDLING&&yn(P.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(kt.attributeNameCheck=P.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),P.CUSTOM_ELEMENT_HANDLING&&typeof P.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(kt.allowCustomizedBuiltInElements=P.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),be&&(ze=!1),Xt&&(de=!0),Te&&(zt=D({},o(k)),Kt=[],Te.html===!0&&(D(zt,K),D(Kt,Z)),Te.svg===!0&&(D(zt,$),D(Kt,nt),D(Kt,ot)),Te.svgFilters===!0&&(D(zt,W),D(Kt,nt),D(Kt,ot)),Te.mathMl===!0&&(D(zt,ut),D(Kt,st),D(Kt,ot))),P.ADD_TAGS&&(zt===te&&(zt=U(zt)),D(zt,P.ADD_TAGS,Jt)),P.ADD_ATTR&&(Kt===Ee&&(Kt=U(Kt)),D(Kt,P.ADD_ATTR,Jt)),P.ADD_URI_SAFE_ATTR&&D(je,P.ADD_URI_SAFE_ATTR,Jt),P.FORBID_CONTENTS&&(Ie===Xe&&(Ie=U(Ie)),D(Ie,P.FORBID_CONTENTS,Jt)),ue&&(zt["#text"]=!0),fe&&D(zt,["html","head","body"]),zt.table&&(D(zt,["tbody"]),delete le.tbody),v&&v(P),Oe=P)}return O}(),Sn=D({},["mi","mo","mn","ms","mtext"]),oe=D({},["foreignobject","desc","title","annotation-xml"]),Ge=D({},["title","style","font","a","script"]),Re=D({},$);D(Re,W),D(Re,tt);var _e=D({},ut);D(_e,ht);var Mn=function(){function O(P){var w=Zt(P);(!w||!w.tagName)&&(w={namespaceURI:Ae,tagName:"template"});var F=T(P.tagName),H=T(w.tagName);return Ze[P.namespaceURI]?P.namespaceURI===Ue?w.namespaceURI===se?F==="svg":w.namespaceURI===ee?F==="svg"&&(H==="annotation-xml"||Sn[H]):!!Re[F]:P.namespaceURI===ee?w.namespaceURI===se?F==="math":w.namespaceURI===Ue?F==="math"&&oe[H]:!!_e[F]:P.namespaceURI===se?w.namespaceURI===Ue&&!oe[H]||w.namespaceURI===ee&&!Sn[H]?!1:!_e[F]&&(Ge[F]||!Re[F]):!!(ce==="application/xhtml+xml"&&Ze[P.namespaceURI]):!1}return O}(),ne=function(){function O(P){y(ct.removed,{element:P});try{P.parentNode.removeChild(P)}catch(w){try{P.outerHTML=Pe}catch(F){P.remove()}}}return O}(),Ye=function(){function O(P,w){try{y(ct.removed,{attribute:w.getAttributeNode(P),from:w})}catch(F){y(ct.removed,{attribute:null,from:w})}if(w.removeAttribute(P),P==="is"&&!Kt[P])if(de||Xt)try{ne(w)}catch(F){}else try{w.setAttribute(P,"")}catch(F){}}return O}(),En=function(){function O(P){var w,F;if(xe)P=""+P;else{var H=M(P,/^[\r\n\t ]+/);F=H&&H[0]}ce==="application/xhtml+xml"&&Ae===se&&(P=''+P+"");var q=_t?_t.createHTML(P):P;if(Ae===se)try{w=new Ft().parseFromString(q,ce)}catch(it){}if(!w||!w.documentElement){w=me.createDocument(Ae,"template",null);try{w.documentElement.innerHTML=Qe?Pe:q}catch(it){}}var _=w.body||w.documentElement;return P&&F&&_.insertBefore(Tt.createTextNode(F),_.childNodes[0]||null),Ae===se?un.call(w,fe?"html":"body")[0]:fe?w.documentElement:_}return O}(),we=function(){function O(P){return ye.call(P.ownerDocument||P,P,gt.SHOW_ELEMENT|gt.SHOW_COMMENT|gt.SHOW_TEXT|gt.SHOW_PROCESSING_INSTRUCTION|gt.SHOW_CDATA_SECTION,null,!1)}return O}(),Rn=function(){function O(P){return P instanceof At&&(typeof P.nodeName!="string"||typeof P.textContent!="string"||typeof P.removeChild!="function"||!(P.attributes instanceof St)||typeof P.removeAttribute!="function"||typeof P.setAttribute!="function"||typeof P.namespaceURI!="string"||typeof P.insertBefore!="function"||typeof P.hasChildNodes!="function")}return O}(),he=function(){function O(P){return e(Ct)==="object"?P instanceof Ct:P&&e(P)==="object"&&typeof P.nodeType=="number"&&typeof P.nodeName=="string"}return O}(),ae=function(){function O(P,w,F){re[P]&&C(re[P],function(H){H.call(ct,w,F,Oe)})}return O}(),Cn=function(){function O(P){var w;if(ae("beforeSanitizeElements",P,null),Rn(P)||x(/[\u0080-\uFFFF]/,P.nodeName))return ne(P),!0;var F=Jt(P.nodeName);if(ae("uponSanitizeElement",P,{tagName:F,allowedTags:zt}),P.hasChildNodes()&&!he(P.firstElementChild)&&(!he(P.content)||!he(P.content.firstElementChild))&&x(/<[/\w]/g,P.innerHTML)&&x(/<[/\w]/g,P.textContent)||F==="select"&&x(/